SPB Git forge

spb/rareindex

Public
54commits 1branches 0releases
7.1 MBsize
maindefault branch
10 days agolast push
TypeScript 61.9% HTML 37.2% SQL 0.7%
3.1 KB · 62 lines typescript
Raw Blame History
1import { readFileSync } from 'node:fs';2import path from 'node:path';3import { describe, expect, it } from 'vitest';4import { getConnectorMeta } from '@rareindex/connectors';5import { fixtureDir, loadFixture, runFixtureSuite } from '@rareindex/connectors/testing';6import createConnector, { itemsUrl, searchUrl, toItem } from './index.js';7import { cjkLanguage, unixToDate } from '../_g9-asia-watch-sneaker-lib/index.js';89const connector = createConnector(getConnectorMeta('ruten'));10const sample = JSON.parse(readFileSync(path.join(fixtureDir('ruten'), 'samples', 'rapi-item.json'), 'utf8')) as { data: Record<string, unknown>[] };1112describe('ruten', () => {13  runFixtureSuite(connector, it, expect);1415  it('maps a raw rapi item (TWD price, stock, store, image, post time)', () => {16    const it0 = toItem(sample.data[0]!);17    expect(it0).not.toBeNull();18    expect(it0!.id).toBe('22629136833498');19    expect(it0!.currency).toBe('TWD');20    expect(it0!.price).toBe(10500);21    expect(it0!.priceMin).toBe(10500);22    expect(it0!.storeName).toBe('91特賣場');23    expect(it0!.class).toBe('000500140015');24    expect(it0!.image).toMatch(/^https:\/\/gcs\.rimg\.com\.tw\//);25    expect(it0!.available).toBe(true);26    expect(unixToDate(it0!.postTime)?.toISOString()).toBe('2026-07-16T04:17:16.000Z'); // post_time 178417543627    expect(toItem({ id: 'x', name: 'no price' })).toBeNull();28  });2930  it('builds the public search and batch item URLs', () => {31    expect(searchUrl('寶可夢 PSA10', 31)).toBe('https://rtapi.ruten.com.tw/api/search/v3/index.php/core/prod?q=%E5%AF%B6%E5%8F%AF%E5%A4%A2%20PSA10&type=direct&sort=rnk%2Fdc&offset=31&limit=30');32    expect(itemsUrl(['1', '2'])).toBe('https://rapi.ruten.com.tw/api/items/v2/list?gno=1,2&level=simple');33  });3435  it('normalises the search fixture into TWD listings with grades, language and attribution URLs', async () => {36    const fx = loadFixture('ruten', 'pokemon-psa10-search-p1');37    const out = await connector.normalize(fx.raw);38    expect(out.length).toBeGreaterThan(5);39    const l = out[0];40    if (l?.kind !== 'listing') throw new Error('expected listing');41    expect(l.currency).toBe('TWD');42    expect(l.price).toBe(10500);43    expect(l.attributes.categorySlug).toBe('pokemon');44    expect(l.attributes.identifiers.ruten_item_id).toBe('22629136833498');45    expect(l.attributes.language).toBe('Chinese');46    expect(l.grade.grader).toBe('psa');47    expect(l.grade.grade).toBe('10');48    expect(l.sourceUrl).toBe('https://www.ruten.com.tw/item/show?22629136833498');49    expect(l.listingType).toBe('fixed_price');50    expect(l.endsAt).toBeNull(); // far-future placeholder deadline is dropped51    expect(l.attributes.metadata.sold_count).toBe(0);52    const sold = out.find((r) => r.kind === 'listing' && r.availability === 'sold');53    expect(sold).toBeDefined(); // stock 0 with sold_num > 0 → sold, never emitted as a sale54  });5556  it('detects card language from Chinese titles', () => {57    expect(cjkLanguage('寶可夢 PTCG 日文版 SM8a 058 SR')).toBe('Japanese');58    expect(cjkLanguage('寶可夢 PTCG 中文版 莉莉艾的全力')).toBe('Chinese');59    expect(cjkLanguage('PTCG 寶可夢 PSA10 竹蘭的霸氣')).toBeNull();60  });61});62