94 行
2.7 KiB
TypeScript
94 行
2.7 KiB
TypeScript
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");
|
|
});
|
|
});
|