mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-02 13:02:49 +08:00
Corner-case review of the #90 play-by-id parser found two reachable issues: - A NetEase playlist/album/artist/toplist/djradio share URL (which reuses ?id=) was matched as a SONG id, so pasting one into !play called getSongDetail() on a collection id and returned a confusing 'No song found' instead of falling back to a normal search. Guard the id= branch to exclude collection pages. - The id: prefix captured trailing punctuation from a chat paste ('id:12345.' -> '12345.'), which then failed to resolve. Strip trailing .,;)] from the id. Both fall back to safe behavior (plain search / clean id). Tests added for collection URLs and pasted ids with punctuation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
70 lines
3.5 KiB
TypeScript
70 lines
3.5 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { parseSongRef, parseSelectionIndex } from "./song-ref.js";
|
|
|
|
describe("parseSongRef (#90 exact-song selection)", () => {
|
|
it("returns null for a plain search term", () => {
|
|
expect(parseSongRef("Die For You")).toBeNull();
|
|
expect(parseSongRef("周杰伦 晴天")).toBeNull();
|
|
expect(parseSongRef("")).toBeNull();
|
|
// A bare number is NOT treated as an id (a song may be named "2002").
|
|
expect(parseSongRef("2002")).toBeNull();
|
|
});
|
|
|
|
it("parses an explicit id: prefix with no platform (defer to flags)", () => {
|
|
expect(parseSongRef("id:185868")).toEqual({ id: "185868", platform: null });
|
|
expect(parseSongRef("ID: 004Z8Ihr0JIu5s")).toEqual({ id: "004Z8Ihr0JIu5s", platform: null });
|
|
});
|
|
|
|
it("strips trailing punctuation from a pasted id:", () => {
|
|
expect(parseSongRef("id:185868.")).toEqual({ id: "185868", platform: null });
|
|
expect(parseSongRef("id:185868)")).toEqual({ id: "185868", platform: null });
|
|
expect(parseSongRef("id:185868,")).toEqual({ id: "185868", platform: null });
|
|
});
|
|
|
|
it("does NOT treat NetEase collection (playlist/album/artist) URLs as a song id", () => {
|
|
// These reuse ?id= but are not songs — they should fall through to search,
|
|
// not misresolve to getSongDetail(collectionId) and error "no song".
|
|
expect(parseSongRef("https://music.163.com/playlist?id=123456")).toBeNull();
|
|
expect(parseSongRef("https://music.163.com/#/playlist?id=123456")).toBeNull();
|
|
expect(parseSongRef("https://music.163.com/album?id=123456")).toBeNull();
|
|
expect(parseSongRef("https://music.163.com/artist?id=185858")).toBeNull();
|
|
// A genuine song URL is still parsed.
|
|
expect(parseSongRef("https://music.163.com/song?id=185868")).toEqual({ id: "185868", platform: "netease" });
|
|
});
|
|
|
|
it("parses NetEase song URLs", () => {
|
|
expect(parseSongRef("https://music.163.com/song?id=185868")).toEqual({ id: "185868", platform: "netease" });
|
|
expect(parseSongRef("https://music.163.com/#/song?id=185868&userid=1")).toEqual({ id: "185868", platform: "netease" });
|
|
expect(parseSongRef("music.163.com/song/185868")).toEqual({ id: "185868", platform: "netease" });
|
|
});
|
|
|
|
it("parses QQ song URLs", () => {
|
|
expect(parseSongRef("https://y.qq.com/n/ryqq/songDetail/004Z8Ihr0JIu5s")).toEqual({ id: "004Z8Ihr0JIu5s", platform: "qq" });
|
|
expect(parseSongRef("https://y.qq.com/n/yqq/song/abc.html?songmid=004Z8Ihr0JIu5s")).toEqual({ id: "004Z8Ihr0JIu5s", platform: "qq" });
|
|
});
|
|
|
|
it("parses BiliBili BV ids (bare or in a URL)", () => {
|
|
expect(parseSongRef("BV1yxHQeYEuE")).toEqual({ id: "BV1yxHQeYEuE", platform: "bilibili" });
|
|
expect(parseSongRef("https://www.bilibili.com/video/BV1yxHQeYEuE")).toEqual({ id: "BV1yxHQeYEuE", platform: "bilibili" });
|
|
expect(parseSongRef("https://b23.tv/BV1yxHQeYEuE")).toEqual({ id: "BV1yxHQeYEuE", platform: "bilibili" });
|
|
});
|
|
});
|
|
|
|
describe("parseSelectionIndex (#90 pick from last search)", () => {
|
|
it("parses #N tokens (1-based)", () => {
|
|
expect(parseSelectionIndex("#1")).toBe(1);
|
|
expect(parseSelectionIndex("#2")).toBe(2);
|
|
expect(parseSelectionIndex("# 3")).toBe(3);
|
|
expect(parseSelectionIndex(" #10 ")).toBe(10);
|
|
});
|
|
|
|
it("rejects non-selections", () => {
|
|
expect(parseSelectionIndex("2")).toBeNull();
|
|
expect(parseSelectionIndex("#0")).toBeNull();
|
|
expect(parseSelectionIndex("#-1")).toBeNull();
|
|
expect(parseSelectionIndex("Die For You")).toBeNull();
|
|
expect(parseSelectionIndex("#2 extra")).toBeNull();
|
|
expect(parseSelectionIndex("")).toBeNull();
|
|
});
|
|
});
|