新需求更新 同步更新
This commit is contained in:
+57
@@ -1,6 +1,7 @@
|
||||
package com.nanri.aiimage.modules.similarasin.client;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.nanri.aiimage.config.SimilarAsinProperties;
|
||||
import com.nanri.aiimage.modules.similarasin.model.dto.SimilarAsinResultRowDto;
|
||||
@@ -180,4 +181,60 @@ class SimilarAsinCozeClientTest {
|
||||
assertTrue(json.contains("\"price\":10"));
|
||||
assertTrue(json.contains("\"price\":80"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void buildParametersIncludesCategorySwitch() throws Exception {
|
||||
SimilarAsinResultRowDto row = new SimilarAsinResultRowDto();
|
||||
row.setAsin("B0CATEGORY1");
|
||||
row.setTitle("Category test");
|
||||
SimilarAsinCozeClient client = new SimilarAsinCozeClient(new SimilarAsinProperties(), objectMapper, null);
|
||||
|
||||
Method method = SimilarAsinCozeClient.class.getDeclaredMethod(
|
||||
"buildParameters", List.class, String.class, String.class, boolean.class, boolean.class);
|
||||
method.setAccessible(true);
|
||||
Map<String, Object> parameters = (Map<String, Object>) method.invoke(client, List.of(row), "", "", false, true);
|
||||
|
||||
assertEquals(Boolean.TRUE, parameters.get("category_switch"));
|
||||
assertEquals(Boolean.FALSE, parameters.get("img_switch"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void imageOnlyWorkflowOutputIsExtractedAndMergedByAsin() throws Exception {
|
||||
SimilarAsinCozeClient client = new SimilarAsinCozeClient(new SimilarAsinProperties(), objectMapper, null);
|
||||
String imageData = """
|
||||
{"data":[{
|
||||
"asin":"B0BQNHDP2F",
|
||||
"main_url":"https://example.com/main.jpg",
|
||||
"puzzle_img1":"https://example.com/puzzle-1.jpg",
|
||||
"puzzle_img2":"https://example.com/puzzle-2.jpg"
|
||||
}]}
|
||||
""";
|
||||
String workflowOutput = objectMapper.writeValueAsString(Map.of(
|
||||
"node_status", "{}",
|
||||
"Output", imageData));
|
||||
var historyResponse = objectMapper.createObjectNode();
|
||||
historyResponse.put("code", 0);
|
||||
historyResponse.putArray("data")
|
||||
.addObject()
|
||||
.put("execute_status", "Success")
|
||||
.put("output", workflowOutput);
|
||||
Method extract = SimilarAsinCozeClient.class.getDeclaredMethod("extractResultDataText", JsonNode.class);
|
||||
extract.setAccessible(true);
|
||||
String dataText = (String) extract.invoke(client, historyResponse);
|
||||
|
||||
SimilarAsinResultRowDto source = new SimilarAsinResultRowDto();
|
||||
source.setAsin("B0BQNHDP2F");
|
||||
List<SimilarAsinResultRowDto> merged = client.mergeRowsFromDataText(List.of(source), dataText);
|
||||
|
||||
assertFalse(dataText.isBlank());
|
||||
assertEquals(1, merged.size());
|
||||
assertEquals("https://example.com/main.jpg", merged.getFirst().getMainUrl());
|
||||
assertEquals("https://example.com/puzzle-1.jpg", merged.getFirst().getPuzzleImg1());
|
||||
assertEquals("https://example.com/puzzle-2.jpg", merged.getFirst().getPuzzleImg2());
|
||||
|
||||
Method resolvedCount = SimilarAsinCozeClient.class.getDeclaredMethod("resolvedCount", List.class);
|
||||
resolvedCount.setAccessible(true);
|
||||
assertEquals(1, resolvedCount.invoke(client, merged));
|
||||
}
|
||||
}
|
||||
|
||||
+53
@@ -1,9 +1,11 @@
|
||||
package com.nanri.aiimage.modules.similarasin.service;
|
||||
|
||||
import com.nanri.aiimage.modules.similarasin.model.dto.SimilarAsinResultRowDto;
|
||||
import com.nanri.aiimage.modules.similarasin.model.vo.SimilarAsinParsedRowVo;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -60,6 +62,57 @@ class SimilarAsinTaskServiceTest {
|
||||
assertFalse(SimilarAsinTaskService.isTerminalFileBuildProgress("SUCCESS", 2, 3));
|
||||
}
|
||||
|
||||
@Test
|
||||
void failedResultRowsWithBlankIsConformEnableCategoryRetry() {
|
||||
SimilarAsinParsedRowVo blankCategory = new SimilarAsinParsedRowVo();
|
||||
blankCategory.setValues(new LinkedHashMap<>());
|
||||
blankCategory.getValues().put("status", "FAILED");
|
||||
blankCategory.getValues().put("is_conform", "");
|
||||
|
||||
assertTrue(SimilarAsinTaskService.shouldEnableCategorySwitchForRetry(
|
||||
List.of("id", "asin", "country", "is_conform", "status"),
|
||||
List.of(blankCategory),
|
||||
true));
|
||||
|
||||
assertFalse(SimilarAsinTaskService.shouldEnableCategorySwitchForRetry(
|
||||
List.of("id", "asin", "country", "is_conform", "status"),
|
||||
List.of(blankCategory),
|
||||
false));
|
||||
|
||||
blankCategory.getValues().put("is_conform", "符合");
|
||||
assertFalse(SimilarAsinTaskService.shouldEnableCategorySwitchForRetry(
|
||||
List.of("id", "asin", "country", "is_conform", "status"),
|
||||
List.of(blankCategory),
|
||||
true));
|
||||
}
|
||||
|
||||
@Test
|
||||
void firstPassImageResultWorkbookKeepsAllRowsForSecondParse() {
|
||||
SimilarAsinParsedRowVo row = new SimilarAsinParsedRowVo();
|
||||
row.setValues(new LinkedHashMap<>());
|
||||
row.getValues().put("状态", "成功");
|
||||
row.getValues().put("是否有货", "");
|
||||
row.getValues().put("相似度", "");
|
||||
row.getValues().put("是否符合类目", "");
|
||||
row.getValues().put("不符合理由", "");
|
||||
row.getValues().put("产品类目", "");
|
||||
|
||||
assertTrue(SimilarAsinTaskService.isFirstPassResultWorkbook(
|
||||
List.of("id", "asin", "国家", "是否有货", "相似度", "是否符合类目", "不符合理由", "产品类目", "状态", "主图", "阿里巴巴图片1", "阿里巴巴图片2"),
|
||||
List.of(row)));
|
||||
|
||||
row.getValues().put("是否符合类目", "符合");
|
||||
assertFalse(SimilarAsinTaskService.isFirstPassResultWorkbook(
|
||||
List.of("id", "asin", "国家", "是否有货", "相似度", "是否符合类目", "不符合理由", "产品类目", "状态", "主图", "阿里巴巴图片1", "阿里巴巴图片2"),
|
||||
List.of(row)));
|
||||
|
||||
row.getValues().put("是否符合类目", "");
|
||||
row.getValues().put("状态", "失败");
|
||||
assertFalse(SimilarAsinTaskService.isFirstPassResultWorkbook(
|
||||
List.of("id", "asin", "国家", "是否有货", "相似度", "是否符合类目", "不符合理由", "产品类目", "状态", "主图", "阿里巴巴图片1", "阿里巴巴图片2"),
|
||||
List.of(row)));
|
||||
}
|
||||
|
||||
private int staticIntField(String name) throws Exception {
|
||||
Field field = SimilarAsinTaskService.class.getDeclaredField(name);
|
||||
field.setAccessible(true);
|
||||
|
||||
+59
@@ -2,6 +2,7 @@ package com.nanri.aiimage.modules.similarasin.util;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -11,7 +12,9 @@ import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ExcelCellImageWriterTest {
|
||||
@@ -42,6 +45,48 @@ class ExcelCellImageWriterTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void streamsRegisteredImagePathIntoWorkbookMedia() throws Exception {
|
||||
Path dir = Files.createTempDirectory("excel-cell-image-path-test-");
|
||||
Path xlsx = dir.resolve("result.xlsx");
|
||||
Path image = dir.resolve("thumb.jpeg");
|
||||
byte[] imageBytes = new byte[]{7, 8, 9, 10};
|
||||
writeMinimalWorkbook(xlsx);
|
||||
Files.write(image, imageBytes);
|
||||
|
||||
ExcelCellImageWriter.Session session = ExcelCellImageWriter.createSession();
|
||||
session.registerImage(1, 9, image);
|
||||
|
||||
ExcelCellImageWriter.patchXlsxFile(xlsx.toFile(), session);
|
||||
|
||||
try (ZipFile zip = new ZipFile(xlsx.toFile())) {
|
||||
assertArrayEquals(imageBytes,
|
||||
zip.getInputStream(zip.getEntry("xl/media/excelcellimage1.jpeg")).readAllBytes());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void patchesTenThousandImageCellsWithinLinearTimeBudget() {
|
||||
assertTimeoutPreemptively(Duration.ofSeconds(10), () -> {
|
||||
Path dir = Files.createTempDirectory("excel-cell-image-linear-test-");
|
||||
Path xlsx = dir.resolve("result.xlsx");
|
||||
writeWorkbookWithImageCells(xlsx, 10_000);
|
||||
|
||||
ExcelCellImageWriter.Session session = ExcelCellImageWriter.createSession();
|
||||
for (int row = 1; row <= 10_000; row++) {
|
||||
session.registerImage(row, 9, new byte[]{1});
|
||||
}
|
||||
|
||||
ExcelCellImageWriter.patchXlsxFile(xlsx.toFile(), session);
|
||||
|
||||
try (ZipFile zip = new ZipFile(xlsx.toFile())) {
|
||||
String sheet = read(zip, "xl/worksheets/sheet1.xml");
|
||||
assertEquals(10_000, maxVm(sheet));
|
||||
assertTrue(sheet.contains("<c r=\"J10001\" t=\"e\" vm=\"10000\"><v>#VALUE!</v></c>"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static int maxVm(String sheet) {
|
||||
Matcher matcher = Pattern.compile("vm=\"(\\d+)\"").matcher(sheet);
|
||||
int max = -1;
|
||||
@@ -99,6 +144,20 @@ class ExcelCellImageWriterTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeWorkbookWithImageCells(Path xlsx, int imageCount) throws Exception {
|
||||
try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(xlsx))) {
|
||||
write(out, "[Content_Types].xml", "<Types></Types>");
|
||||
write(out, "xl/_rels/workbook.xml.rels", "<Relationships></Relationships>");
|
||||
StringBuilder sheet = new StringBuilder("<worksheet><sheetData>");
|
||||
for (int row = 2; row <= imageCount + 1; row++) {
|
||||
sheet.append("<row r=\"").append(row).append("\"><c r=\"J").append(row)
|
||||
.append("\" t=\"inlineStr\"><is><t>image</t></is></c></row>");
|
||||
}
|
||||
sheet.append("</sheetData></worksheet>");
|
||||
write(out, "xl/worksheets/sheet1.xml", sheet.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void write(ZipOutputStream out, String name, String content) throws Exception {
|
||||
out.putNextEntry(new ZipEntry(name));
|
||||
out.write(content.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
+222
-1
@@ -1,7 +1,15 @@
|
||||
package com.nanri.aiimage.modules.similarasin.util;
|
||||
|
||||
import com.nanri.aiimage.config.OssProperties;
|
||||
import com.nanri.aiimage.config.SimilarAsinProperties;
|
||||
import com.nanri.aiimage.modules.file.service.oss.OssStorageService;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Protocol;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
@@ -10,24 +18,39 @@ import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
||||
class SimilarAsinImageEmbedderTest {
|
||||
|
||||
// properties=null 时构造函数走 DEFAULT_DOWNLOAD_TIMEOUT_SECONDS / DEFAULT_DOWNLOAD_POOL_SIZE 兜底。
|
||||
private final SimilarAsinImageEmbedder embedder = new SimilarAsinImageEmbedder(null, createOssStorageService());
|
||||
|
||||
@AfterEach
|
||||
void shutDownEmbedder() {
|
||||
embedder.shutdown();
|
||||
}
|
||||
|
||||
private static OssStorageService createOssStorageService() {
|
||||
OssProperties properties = new OssProperties();
|
||||
properties.setEndpoint("https://oss.aishufu.top");
|
||||
@@ -40,6 +63,133 @@ class SimilarAsinImageEmbedderTest {
|
||||
return new OssStorageService(properties);
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultsImageDownloadPoolToEight() {
|
||||
assertEquals(8, new SimilarAsinProperties().getImageDownloadPoolSize());
|
||||
assertEquals(8, embedder.downloadPoolSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
void writesFetchedThumbnailToPersistentCacheForNextEmbedderInstance() throws Exception {
|
||||
Path cacheDir = Files.createTempDirectory("similar-asin-image-cache-test-");
|
||||
String url = "https://images.example.com/product.jpg";
|
||||
byte[] sourceImage = createJpegBytes();
|
||||
AtomicInteger firstNetworkCalls = new AtomicInteger();
|
||||
AtomicInteger secondNetworkCalls = new AtomicInteger();
|
||||
SimilarAsinImageEmbedder first = new SimilarAsinImageEmbedder(
|
||||
properties(1, 5, cacheDir), createOssStorageService());
|
||||
SimilarAsinImageEmbedder second = new SimilarAsinImageEmbedder(
|
||||
properties(1, 5, cacheDir), createOssStorageService());
|
||||
try {
|
||||
replaceHttpClient(first, respondingClient(firstNetworkCalls, sourceImage));
|
||||
SimilarAsinImageEmbedder.ResizedImage fetched = first.fetchAndResizeForCache(url);
|
||||
|
||||
assertNotNull(fetched);
|
||||
assertEquals(1, firstNetworkCalls.get());
|
||||
try (var cachedFiles = Files.walk(cacheDir)) {
|
||||
assertEquals(1L, cachedFiles.filter(Files::isRegularFile).count());
|
||||
}
|
||||
|
||||
replaceHttpClient(second, new OkHttpClient.Builder()
|
||||
.addInterceptor(chain -> {
|
||||
secondNetworkCalls.incrementAndGet();
|
||||
throw new AssertionError("persistent cache miss triggered a second network request");
|
||||
})
|
||||
.build());
|
||||
SimilarAsinImageEmbedder.ResizedImage cached = second.fetchAndResizeForCache(url);
|
||||
|
||||
assertNotNull(cached);
|
||||
assertArrayEquals(fetched.bytes(), cached.bytes());
|
||||
assertEquals(fetched.width(), cached.width());
|
||||
assertEquals(fetched.height(), cached.height());
|
||||
assertEquals(0, secondNetworkCalls.get());
|
||||
} finally {
|
||||
first.shutdown();
|
||||
second.shutdown();
|
||||
deleteRecursively(cacheDir);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void diskPrefetchDeadlineCancelsInFlightDownload() throws Exception {
|
||||
SimilarAsinImageEmbedder deadlineEmbedder = new SimilarAsinImageEmbedder(
|
||||
properties(1, 1, null), createOssStorageService());
|
||||
CountDownLatch downloadStarted = new CountDownLatch(1);
|
||||
CountDownLatch cancellationObserved = new CountDownLatch(1);
|
||||
replaceHttpClient(deadlineEmbedder, new OkHttpClient.Builder()
|
||||
.addInterceptor(chain -> {
|
||||
downloadStarted.countDown();
|
||||
try {
|
||||
new CountDownLatch(1).await();
|
||||
throw new AssertionError("blocking download unexpectedly completed");
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
cancellationObserved.countDown();
|
||||
throw new IOException("cancelled", ex);
|
||||
}
|
||||
})
|
||||
.build());
|
||||
Path spoolDir = Files.createTempDirectory("similar-asin-prefetch-deadline-test-");
|
||||
try (SimilarAsinImageEmbedder.ImageSpool spool = new SimilarAsinImageEmbedder.ImageSpool(spoolDir)) {
|
||||
assertTimeoutPreemptively(Duration.ofSeconds(3), () -> deadlineEmbedder.prefetchToDisk(
|
||||
List.of("https://images.example.com/slow.jpg"), spool));
|
||||
|
||||
assertTrue(downloadStarted.await(100, TimeUnit.MILLISECONDS));
|
||||
assertTrue(cancellationObserved.await(1, TimeUnit.SECONDS),
|
||||
"deadline should interrupt the active image download");
|
||||
assertEquals(0, spool.size());
|
||||
} finally {
|
||||
deadlineEmbedder.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void failedDiskPrefetchIsNotDownloadedAgainWhileEmbedding() throws Exception {
|
||||
SimilarAsinImageEmbedder failedEmbedder = new SimilarAsinImageEmbedder(
|
||||
properties(1, 5, null), createOssStorageService());
|
||||
AtomicInteger networkCalls = new AtomicInteger();
|
||||
replaceHttpClient(failedEmbedder, failingClient(networkCalls));
|
||||
String url = "https://images.example.com/missing.jpg";
|
||||
Path spoolDir = Files.createTempDirectory("similar-asin-prefetch-failed-test-");
|
||||
try (SimilarAsinImageEmbedder.ImageSpool spool = new SimilarAsinImageEmbedder.ImageSpool(spoolDir);
|
||||
XSSFWorkbook workbook = new XSSFWorkbook()) {
|
||||
failedEmbedder.prefetchToDisk(List.of(url), spool);
|
||||
int callsAfterPrefetch = networkCalls.get();
|
||||
var row = workbook.createSheet().createRow(1);
|
||||
|
||||
SimilarAsinImageEmbedder.ImageDim result = failedEmbedder.embedAsExcelCellImage(
|
||||
1, 9, url, row, new HashMap<>(), ExcelCellImageWriter.createSession(), spool);
|
||||
|
||||
assertEquals(SimilarAsinImageEmbedder.DOWNLOAD_MAX_RETRY + 1, callsAfterPrefetch);
|
||||
assertEquals(callsAfterPrefetch, networkCalls.get());
|
||||
assertNull(result);
|
||||
assertEquals(url, row.getCell(9).getStringCellValue());
|
||||
} finally {
|
||||
failedEmbedder.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void imageSpoolWritesThumbnailAndDeletesTaskDirectoryOnClose() throws Exception {
|
||||
Path directory = Files.createTempDirectory("similar-asin-image-spool-test-");
|
||||
byte[] bytes = new byte[]{1, 3, 5, 7};
|
||||
SimilarAsinImageEmbedder.ImageSpool spool = new SimilarAsinImageEmbedder.ImageSpool(directory);
|
||||
try {
|
||||
SimilarAsinImageEmbedder.SpoolImage image = spool.put(
|
||||
"https://example.com/image.jpg",
|
||||
new SimilarAsinImageEmbedder.ResizedImage(bytes, 120, 80));
|
||||
|
||||
assertEquals(1, spool.size());
|
||||
assertEquals(120, image.width());
|
||||
assertEquals(80, image.height());
|
||||
assertArrayEquals(bytes, Files.readAllBytes(image.path()));
|
||||
} finally {
|
||||
spool.close();
|
||||
}
|
||||
|
||||
assertFalse(Files.exists(directory));
|
||||
}
|
||||
|
||||
@Test
|
||||
void normalizesLegacyMinioUrlBeforeHttpsValidation() {
|
||||
String normalized = embedder.normalizeAndValidateDownloadUrl(
|
||||
@@ -247,4 +397,75 @@ class SimilarAsinImageEmbedderTest {
|
||||
assertTrue(summary.contains("IOException: outer"));
|
||||
assertTrue(summary.contains("cause=TimeoutException: inner"));
|
||||
}
|
||||
|
||||
private static SimilarAsinProperties properties(int poolSize, int prefetchTimeoutSeconds, Path cacheDir) {
|
||||
SimilarAsinProperties properties = new SimilarAsinProperties();
|
||||
properties.setImageDownloadPoolSize(poolSize);
|
||||
properties.setImagePrefetchTimeoutSeconds(prefetchTimeoutSeconds);
|
||||
properties.setImageLocalCacheDir(cacheDir == null ? "" : cacheDir.toString());
|
||||
return properties;
|
||||
}
|
||||
|
||||
private static OkHttpClient respondingClient(AtomicInteger calls, byte[] imageBytes) {
|
||||
return new OkHttpClient.Builder()
|
||||
.addInterceptor(chain -> {
|
||||
calls.incrementAndGet();
|
||||
return response(chain.request(), 200, "OK", imageBytes);
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
private static OkHttpClient failingClient(AtomicInteger calls) {
|
||||
return new OkHttpClient.Builder()
|
||||
.addInterceptor(chain -> {
|
||||
calls.incrementAndGet();
|
||||
return response(chain.request(), 503, "Unavailable", new byte[0]);
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
private static Response response(okhttp3.Request request,
|
||||
int status,
|
||||
String message,
|
||||
byte[] body) {
|
||||
return new Response.Builder()
|
||||
.request(request)
|
||||
.protocol(Protocol.HTTP_1_1)
|
||||
.code(status)
|
||||
.message(message)
|
||||
.body(ResponseBody.create(body, MediaType.get("image/jpeg")))
|
||||
.build();
|
||||
}
|
||||
|
||||
private static void replaceHttpClient(SimilarAsinImageEmbedder target,
|
||||
OkHttpClient httpClient) throws ReflectiveOperationException {
|
||||
Field field = SimilarAsinImageEmbedder.class.getDeclaredField("httpClient");
|
||||
field.setAccessible(true);
|
||||
field.set(target, httpClient);
|
||||
}
|
||||
|
||||
private static byte[] createJpegBytes() throws IOException {
|
||||
BufferedImage image = new BufferedImage(64, 48, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D graphics = image.createGraphics();
|
||||
try {
|
||||
graphics.setColor(new Color(0x24, 0x68, 0xAC));
|
||||
graphics.fillRect(0, 0, image.getWidth(), image.getHeight());
|
||||
} finally {
|
||||
graphics.dispose();
|
||||
}
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
ImageIO.write(image, "jpg", output);
|
||||
return output.toByteArray();
|
||||
}
|
||||
|
||||
private static void deleteRecursively(Path directory) throws IOException {
|
||||
if (!Files.exists(directory)) {
|
||||
return;
|
||||
}
|
||||
try (var paths = Files.walk(directory)) {
|
||||
for (Path path : paths.sorted(java.util.Comparator.reverseOrder()).toList()) {
|
||||
Files.deleteIfExists(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user