feat(spotify): implement milestone M3 api commands
This commit is contained in:
158
skills/spotify/tests/playlists.test.ts
Normal file
158
skills/spotify/tests/playlists.test.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "node:test";
|
||||
|
||||
import {
|
||||
runAddToPlaylistCommand,
|
||||
runCreatePlaylistCommand,
|
||||
runRemoveFromPlaylistCommand,
|
||||
runSearchAndAddCommand,
|
||||
searchAndAdd,
|
||||
validateTrackUris
|
||||
} from "../src/playlists.js";
|
||||
import type { CliDeps } from "../src/cli.js";
|
||||
import type { SpotifyPlaylist } from "../src/types.js";
|
||||
|
||||
function createDeps(): { deps: CliDeps; stdout: () => string; stderr: () => string } {
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
return {
|
||||
deps: {
|
||||
stdout: { write: (chunk: string) => { stdout += chunk; return true; } },
|
||||
stderr: { write: (chunk: string) => { stderr += chunk; return true; } }
|
||||
},
|
||||
stdout: () => stdout,
|
||||
stderr: () => stderr
|
||||
};
|
||||
}
|
||||
|
||||
function playlist(name: string): SpotifyPlaylist {
|
||||
return {
|
||||
id: "playlist-id",
|
||||
uri: "spotify:playlist:playlist-id",
|
||||
name,
|
||||
public: false,
|
||||
owner: { id: "owner-id" },
|
||||
external_urls: { spotify: "https://open.spotify.com/playlist/playlist-id" }
|
||||
};
|
||||
}
|
||||
|
||||
test("create playlist defaults private unless public flag is present", async () => {
|
||||
const io = createDeps();
|
||||
let observed: { name: string; public?: boolean; description?: string } | undefined;
|
||||
|
||||
await runCreatePlaylistCommand(
|
||||
{ command: "create-playlist", positional: ["My", "Mix"], json: true, public: false, description: "desc" },
|
||||
io.deps,
|
||||
{
|
||||
createPlaylist: async (name, options) => {
|
||||
observed = { name, ...options };
|
||||
return playlist(name);
|
||||
},
|
||||
addItemsToPlaylist: async () => [],
|
||||
removeItemsFromPlaylist: async () => [],
|
||||
searchTracks: async () => []
|
||||
}
|
||||
);
|
||||
|
||||
assert.deepEqual(observed, { name: "My Mix", public: false, description: "desc" });
|
||||
assert.equal(JSON.parse(io.stdout()).playlist.id, "playlist-id");
|
||||
});
|
||||
|
||||
test("create playlist treats omitted public flag as private", async () => {
|
||||
const io = createDeps();
|
||||
let observedPublic: boolean | undefined;
|
||||
|
||||
await runCreatePlaylistCommand(
|
||||
{ command: "create-playlist", positional: ["My Mix"], json: true },
|
||||
io.deps,
|
||||
{
|
||||
createPlaylist: async (name, options) => {
|
||||
observedPublic = options?.public;
|
||||
return playlist(name);
|
||||
},
|
||||
addItemsToPlaylist: async () => [],
|
||||
removeItemsFromPlaylist: async () => [],
|
||||
searchTracks: async () => []
|
||||
}
|
||||
);
|
||||
|
||||
assert.equal(observedPublic, false);
|
||||
});
|
||||
|
||||
test("validates spotify track uris", () => {
|
||||
assert.deepEqual(validateTrackUris(["spotify:track:1"]), ["spotify:track:1"]);
|
||||
assert.throws(() => validateTrackUris(["spotify:album:1"]), /Invalid Spotify track URI/);
|
||||
});
|
||||
|
||||
test("add to playlist outputs mutation summary", async () => {
|
||||
const io = createDeps();
|
||||
let observedUris: string[] = [];
|
||||
await runAddToPlaylistCommand(
|
||||
{ command: "add-to-playlist", positional: ["playlist-id", "spotify:track:1"], json: true, public: false },
|
||||
io.deps,
|
||||
{
|
||||
createPlaylist: async () => playlist("unused"),
|
||||
addItemsToPlaylist: async (_playlistId, uris) => {
|
||||
observedUris = uris;
|
||||
return [{ snapshot_id: "snap" }];
|
||||
},
|
||||
removeItemsFromPlaylist: async () => [],
|
||||
searchTracks: async () => []
|
||||
}
|
||||
);
|
||||
|
||||
assert.deepEqual(observedUris, ["spotify:track:1"]);
|
||||
assert.deepEqual(JSON.parse(io.stdout()), { playlistId: "playlist-id", count: 1, snapshotIds: ["snap"] });
|
||||
});
|
||||
|
||||
test("remove from playlist outputs mutation summary", async () => {
|
||||
const io = createDeps();
|
||||
await runRemoveFromPlaylistCommand(
|
||||
{ command: "remove-from-playlist", positional: ["playlist-id", "spotify:track:1"], json: false, public: false },
|
||||
io.deps,
|
||||
{
|
||||
createPlaylist: async () => playlist("unused"),
|
||||
addItemsToPlaylist: async () => [],
|
||||
removeItemsFromPlaylist: async () => [{ snapshot_id: "snap" }],
|
||||
searchTracks: async () => []
|
||||
}
|
||||
);
|
||||
|
||||
assert.equal(io.stdout(), "Removed 1 track(s) from playlist-id.\n");
|
||||
});
|
||||
|
||||
test("searchAndAdd adds first matches and records misses", async () => {
|
||||
let observedUris: string[] = [];
|
||||
const result = await searchAndAdd("playlist-id", ["found", "missing"], {
|
||||
searchTracks: async (query) => query === "found"
|
||||
? [{ id: "track-id", uri: "spotify:track:track-id", name: "Song", artists: [{ name: "Artist" }] }]
|
||||
: [],
|
||||
addItemsToPlaylist: async (_playlistId, uris) => {
|
||||
observedUris = uris;
|
||||
return [{ snapshot_id: "snap" }];
|
||||
}
|
||||
});
|
||||
|
||||
assert.deepEqual(observedUris, ["spotify:track:track-id"]);
|
||||
assert.deepEqual(result, {
|
||||
added: [{ query: "found", uri: "spotify:track:track-id", name: "Song", artists: ["Artist"] }],
|
||||
missed: ["missing"],
|
||||
snapshotIds: ["snap"]
|
||||
});
|
||||
});
|
||||
|
||||
test("search-and-add command writes JSON summary", async () => {
|
||||
const io = createDeps();
|
||||
await runSearchAndAddCommand(
|
||||
{ command: "search-and-add", positional: ["playlist-id", "found"], json: true, public: false },
|
||||
io.deps,
|
||||
{
|
||||
createPlaylist: async () => playlist("unused"),
|
||||
addItemsToPlaylist: async () => [{ snapshot_id: "snap" }],
|
||||
removeItemsFromPlaylist: async () => [],
|
||||
searchTracks: async () => [{ id: "track-id", uri: "spotify:track:track-id", name: "Song", artists: [{ name: "Artist" }] }]
|
||||
}
|
||||
);
|
||||
|
||||
assert.equal(JSON.parse(io.stdout()).added[0].uri, "spotify:track:track-id");
|
||||
});
|
||||
Reference in New Issue
Block a user