Add market watch and match hub workflows
这个提交包含在:
93
server/market.test.ts
普通文件
93
server/market.test.ts
普通文件
@@ -0,0 +1,93 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildMarketSearchQuery,
|
||||
enrichRacketListing,
|
||||
formatMarketPushText,
|
||||
listingMatchesWatchRule,
|
||||
maskWebhookUrl,
|
||||
} from "./market";
|
||||
|
||||
describe("market enrichment", () => {
|
||||
it("extracts brand, model, weight, category and grade from a racket listing", () => {
|
||||
const listing = enrichRacketListing({
|
||||
source: "xianyu",
|
||||
sourceListingId: "xy-1",
|
||||
title: "Yonex Ezone 98 305g 95新 网球拍",
|
||||
listingUrl: "https://www.goofish.com/item?id=xy-1",
|
||||
price: 480,
|
||||
originalPrice: 1200,
|
||||
description: "尤尼克斯 Ezone 98 裸拍约305g,正常使用,无裂纹",
|
||||
imageUrl: null,
|
||||
sellerName: "seller-a",
|
||||
location: "上海",
|
||||
publishedAtRaw: "1小时前",
|
||||
extra: null,
|
||||
});
|
||||
|
||||
expect(listing.brand).toBe("Yonex");
|
||||
expect(listing.series).toBe("Ezone");
|
||||
expect(listing.model).toContain("98");
|
||||
expect(listing.weightGram).toBe(305);
|
||||
expect(listing.category).toBe("competitive");
|
||||
expect(listing.gradeLevel).toBe("high_value");
|
||||
expect(listing.isLowPriceCandidate).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("market watch matching", () => {
|
||||
it("matches a listing when price and keywords satisfy the rule", () => {
|
||||
const matched = listingMatchesWatchRule({
|
||||
price: 520,
|
||||
brand: "Wilson",
|
||||
title: "Wilson Blade 98 V8 网球拍",
|
||||
model: "98 V8",
|
||||
series: "Blade",
|
||||
category: "competitive",
|
||||
weightGram: 305,
|
||||
}, {
|
||||
brand: "Wilson",
|
||||
modelKeyword: "98",
|
||||
seriesKeyword: "Blade",
|
||||
category: "competitive",
|
||||
weightMinGram: 300,
|
||||
weightMaxGram: 310,
|
||||
targetPrice: 550,
|
||||
});
|
||||
|
||||
expect(matched).toBe(true);
|
||||
expect(buildMarketSearchQuery({
|
||||
brand: "Wilson",
|
||||
modelKeyword: "98",
|
||||
seriesKeyword: "Blade",
|
||||
category: "competitive",
|
||||
})).toContain("Wilson");
|
||||
});
|
||||
});
|
||||
|
||||
describe("market push formatting", () => {
|
||||
it("masks webhook urls and formats notification text", () => {
|
||||
expect(maskWebhookUrl("https://open.larksuite.com/open-apis/bot/v2/hook/1234567890abcdef"))
|
||||
.toContain("...");
|
||||
|
||||
const text = formatMarketPushText({
|
||||
ruleTitle: "Yonex Ezone ≤ ¥500",
|
||||
source: "xianyu",
|
||||
title: "Yonex Ezone 98 305g",
|
||||
price: 480,
|
||||
targetPrice: 500,
|
||||
brand: "Yonex",
|
||||
model: "98",
|
||||
category: "competitive",
|
||||
weightGram: 305,
|
||||
gradeLevel: "high_value",
|
||||
gradeReason: "品牌 Yonex · 当前价格落在低价区间",
|
||||
listingUrl: "https://www.goofish.com/item?id=1",
|
||||
fetchedAt: "2026-03-23T08:00:00.000Z",
|
||||
});
|
||||
|
||||
expect(text).toContain("命中监控");
|
||||
expect(text).toContain("闲鱼");
|
||||
expect(text).toContain("Yonex");
|
||||
expect(text).toContain("https://www.goofish.com/item?id=1");
|
||||
});
|
||||
});
|
||||
在新工单中引用
屏蔽一个用户