Formatting was configured but never enforced: `bun run format:check` reported 199 unformatted files and ran in no workflow and in no git hook, so .prettierrc (printWidth 100, trailing commas) described an intention rather than the tree. This is the one-time sweep that makes the check gateable. Whitespace and token-reflow only -- no behavioural change: `bun run check` reports 0 errors and all 1053 frontend tests pass before and after. Kept out of every other commit on purpose. A 199-file diff mixed with real changes is unreviewable, and the next commit turns format:check into a hard CI gate so this cannot silently accumulate again.
631 lines
21 KiB
TypeScript
631 lines
21 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
import { invoke } from "@tauri-apps/api/core";
|
|
import { RepositoryClient } from "./repository-client";
|
|
|
|
vi.mock("@tauri-apps/api/core");
|
|
|
|
const mockInvoke = vi.mocked(invoke);
|
|
|
|
describe("RepositoryClient", () => {
|
|
let client: RepositoryClient;
|
|
|
|
beforeEach(() => {
|
|
client = new RepositoryClient();
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.resetAllMocks();
|
|
});
|
|
|
|
describe("Initialization", () => {
|
|
it("should initialize with no handle", () => {
|
|
expect(() => client.getHandle()).toThrow("Repository not initialized");
|
|
});
|
|
|
|
it("should create repository with invoke command", async () => {
|
|
const mockHandle = "test-handle-123";
|
|
(invoke as any).mockResolvedValueOnce(mockHandle);
|
|
|
|
const handle = await client.create("https://server.com", "user1", "token123", "server1");
|
|
|
|
expect(handle).toBe(mockHandle);
|
|
expect(invoke).toHaveBeenCalledWith("repository_create", {
|
|
serverUrl: "https://server.com",
|
|
userId: "user1",
|
|
accessToken: "token123",
|
|
serverId: "server1",
|
|
});
|
|
});
|
|
|
|
it("should store handle after creation", async () => {
|
|
const mockHandle = "test-handle-456";
|
|
(invoke as any).mockResolvedValueOnce(mockHandle);
|
|
|
|
await client.create("https://server.com", "user1", "token123", "server1");
|
|
|
|
expect(client.getHandle()).toBe(mockHandle);
|
|
});
|
|
|
|
it("should destroy repository and clear handle", async () => {
|
|
const mockHandle = "test-handle-789";
|
|
(invoke as any).mockResolvedValueOnce(mockHandle);
|
|
await client.create("https://server.com", "user1", "token123", "server1");
|
|
|
|
(invoke as any).mockResolvedValueOnce(undefined);
|
|
await client.destroy();
|
|
|
|
expect(() => client.getHandle()).toThrow("Repository not initialized");
|
|
expect(invoke).toHaveBeenCalledWith("repository_destroy", { handle: mockHandle });
|
|
});
|
|
});
|
|
|
|
describe("Image URL Methods", () => {
|
|
beforeEach(async () => {
|
|
(invoke as any).mockResolvedValueOnce("test-handle-123");
|
|
await client.create("https://server.com", "user1", "token123", "server1");
|
|
});
|
|
|
|
it("should get image URL from backend", async () => {
|
|
const mockUrl = "https://server.com/Items/item123/Images/Primary?maxWidth=300&api_key=token";
|
|
(invoke as any).mockResolvedValueOnce(mockUrl);
|
|
|
|
const imageUrl = await client.getImageUrl("item123", "Primary", { maxWidth: 300 });
|
|
|
|
expect(imageUrl).toBe(mockUrl);
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_image_url", {
|
|
handle: "test-handle-123",
|
|
itemId: "item123",
|
|
imageType: "Primary",
|
|
options: { maxWidth: 300 },
|
|
});
|
|
});
|
|
|
|
it("should use default image type if not provided", async () => {
|
|
const mockUrl = "https://server.com/Items/item123/Images/Primary?api_key=token";
|
|
(invoke as any).mockResolvedValueOnce(mockUrl);
|
|
|
|
await client.getImageUrl("item123");
|
|
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_image_url", {
|
|
handle: "test-handle-123",
|
|
itemId: "item123",
|
|
imageType: "Primary",
|
|
options: null,
|
|
});
|
|
});
|
|
|
|
it("should pass multiple image options to backend", async () => {
|
|
const mockUrl =
|
|
"https://server.com/Items/item123/Images/Backdrop?maxWidth=1920&maxHeight=1080&quality=90&api_key=token";
|
|
(invoke as any).mockResolvedValueOnce(mockUrl);
|
|
|
|
const options = {
|
|
maxWidth: 1920,
|
|
maxHeight: 1080,
|
|
quality: 90,
|
|
tag: "abc123",
|
|
};
|
|
|
|
await client.getImageUrl("item123", "Backdrop", options);
|
|
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_image_url", {
|
|
handle: "test-handle-123",
|
|
itemId: "item123",
|
|
imageType: "Backdrop",
|
|
options,
|
|
});
|
|
});
|
|
|
|
it("should handle different image types", async () => {
|
|
const mockUrl = "https://server.com/Items/item123/Images/Logo?api_key=token";
|
|
(invoke as any).mockResolvedValueOnce(mockUrl);
|
|
|
|
await client.getImageUrl("item123", "Logo");
|
|
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_image_url", {
|
|
handle: expect.any(String),
|
|
itemId: "item123",
|
|
imageType: "Logo",
|
|
options: null,
|
|
});
|
|
});
|
|
|
|
it("should throw error if not initialized before getImageUrl", async () => {
|
|
const newClient = new RepositoryClient();
|
|
|
|
await expect(newClient.getImageUrl("item123")).rejects.toThrow("Repository not initialized");
|
|
});
|
|
});
|
|
|
|
describe("Subtitle URL Methods", () => {
|
|
beforeEach(async () => {
|
|
(invoke as any).mockResolvedValueOnce("test-handle-123");
|
|
await client.create("https://server.com", "user1", "token123", "server1");
|
|
});
|
|
|
|
it("should get subtitle URL from backend", async () => {
|
|
const mockUrl = "https://server.com/Videos/item123/Subtitles/1/subtitles.vtt?api_key=token";
|
|
(invoke as any).mockResolvedValueOnce(mockUrl);
|
|
|
|
const subtitleUrl = await client.getSubtitleUrl("item123", "source456", 0);
|
|
|
|
expect(subtitleUrl).toBe(mockUrl);
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_subtitle_url", {
|
|
handle: "test-handle-123",
|
|
itemId: "item123",
|
|
mediaSourceId: "source456",
|
|
streamIndex: 0,
|
|
format: "vtt",
|
|
});
|
|
});
|
|
|
|
it("should use default format if not provided", async () => {
|
|
const mockUrl = "https://server.com/Videos/item123/Subtitles/0/subtitles.vtt?api_key=token";
|
|
(invoke as any).mockResolvedValueOnce(mockUrl);
|
|
|
|
await client.getSubtitleUrl("item123", "source456", 0);
|
|
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_subtitle_url", {
|
|
handle: expect.any(String),
|
|
itemId: "item123",
|
|
mediaSourceId: "source456",
|
|
streamIndex: 0,
|
|
format: "vtt",
|
|
});
|
|
});
|
|
|
|
it("should support custom subtitle formats", async () => {
|
|
const mockUrl = "https://server.com/Videos/item123/Subtitles/0/subtitles.srt?api_key=token";
|
|
(invoke as any).mockResolvedValueOnce(mockUrl);
|
|
|
|
await client.getSubtitleUrl("item123", "source456", 1, "srt");
|
|
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_subtitle_url", {
|
|
handle: expect.any(String),
|
|
itemId: "item123",
|
|
mediaSourceId: "source456",
|
|
streamIndex: 1,
|
|
format: "srt",
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Video Download URL Methods", () => {
|
|
beforeEach(async () => {
|
|
(invoke as any).mockResolvedValueOnce("test-handle-123");
|
|
await client.create("https://server.com", "user1", "token123", "server1");
|
|
});
|
|
|
|
it("should get video download URL from backend", async () => {
|
|
const mockUrl = "https://server.com/Videos/item123/stream.mp4?maxWidth=1920&api_key=token";
|
|
(invoke as any).mockResolvedValueOnce(mockUrl);
|
|
|
|
const downloadUrl = await client.getVideoDownloadUrl("item123", "high");
|
|
|
|
expect(downloadUrl).toBe(mockUrl);
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_video_download_url", {
|
|
handle: "test-handle-123",
|
|
itemId: "item123",
|
|
quality: "high",
|
|
mediaSourceId: null,
|
|
});
|
|
});
|
|
|
|
it("should use original quality by default", async () => {
|
|
const mockUrl = "https://server.com/Videos/item123/stream.mp4?api_key=token";
|
|
(invoke as any).mockResolvedValueOnce(mockUrl);
|
|
|
|
await client.getVideoDownloadUrl("item123");
|
|
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_video_download_url", {
|
|
handle: expect.any(String),
|
|
itemId: "item123",
|
|
quality: "original",
|
|
mediaSourceId: null,
|
|
});
|
|
});
|
|
|
|
it("should support quality presets", async () => {
|
|
const qualities = ["original", "high", "medium", "low"];
|
|
|
|
for (const quality of qualities) {
|
|
vi.clearAllMocks();
|
|
(invoke as any).mockResolvedValueOnce("test-handle-123");
|
|
await client.create("https://server.com", "user1", "token123", "server1");
|
|
|
|
(invoke as any).mockResolvedValueOnce(`https://server.com/stream.mp4?quality=${quality}`);
|
|
|
|
await client.getVideoDownloadUrl("item123", quality as any);
|
|
|
|
expect(invoke).toHaveBeenCalledWith(
|
|
"repository_get_video_download_url",
|
|
expect.objectContaining({
|
|
quality,
|
|
}),
|
|
);
|
|
}
|
|
});
|
|
|
|
it("should support optional media source ID", async () => {
|
|
const mockUrl = "https://server.com/Videos/item123/stream.mp4?api_key=token";
|
|
(invoke as any).mockResolvedValueOnce(mockUrl);
|
|
|
|
await client.getVideoDownloadUrl("item123", "medium", "source789");
|
|
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_video_download_url", {
|
|
handle: expect.any(String),
|
|
itemId: "item123",
|
|
quality: "medium",
|
|
mediaSourceId: "source789",
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Library Methods", () => {
|
|
beforeEach(async () => {
|
|
(invoke as any).mockResolvedValueOnce("test-handle-123");
|
|
await client.create("https://server.com", "user1", "token123", "server1");
|
|
});
|
|
|
|
it("should get libraries from backend", async () => {
|
|
const mockLibraries = [
|
|
{ id: "lib1", name: "Music", collectionType: "music" },
|
|
{ id: "lib2", name: "Movies", collectionType: "movies" },
|
|
];
|
|
(invoke as any).mockResolvedValueOnce(mockLibraries);
|
|
|
|
const libraries = await client.getLibraries();
|
|
|
|
expect(libraries).toEqual(mockLibraries);
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_libraries", {
|
|
handle: "test-handle-123",
|
|
});
|
|
});
|
|
|
|
it("should get items with sorting parameters", async () => {
|
|
const mockResult = {
|
|
items: [
|
|
{ id: "item1", name: "Track 1", type: "Audio" },
|
|
{ id: "item2", name: "Track 2", type: "Audio" },
|
|
],
|
|
totalRecordCount: 2,
|
|
};
|
|
(invoke as any).mockResolvedValueOnce(mockResult);
|
|
|
|
const result = await client.getItems("library123", {
|
|
sortBy: "SortName",
|
|
sortOrder: "Ascending",
|
|
limit: 50,
|
|
});
|
|
|
|
expect(result).toEqual(mockResult);
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_items", {
|
|
handle: "test-handle-123",
|
|
parentId: "library123",
|
|
options: {
|
|
sortBy: "SortName",
|
|
sortOrder: "Ascending",
|
|
limit: 50,
|
|
},
|
|
});
|
|
});
|
|
|
|
it("should search with backend search command", async () => {
|
|
const mockResult = {
|
|
items: [{ id: "item1", name: "Search Result 1", type: "Audio" }],
|
|
totalRecordCount: 1,
|
|
};
|
|
(invoke as any).mockResolvedValueOnce(mockResult);
|
|
|
|
const result = await client.search("query", {
|
|
includeItemTypes: ["Audio"],
|
|
limit: 100,
|
|
});
|
|
|
|
expect(result).toEqual(mockResult);
|
|
expect(invoke).toHaveBeenCalledWith("repository_search", {
|
|
handle: "test-handle-123",
|
|
query: "query",
|
|
options: {
|
|
includeItemTypes: ["Audio"],
|
|
limit: 100,
|
|
},
|
|
requestId: 0,
|
|
});
|
|
});
|
|
|
|
// Downloaded-only browse path (UR-055 | DR-082) — verifies command names and
|
|
// camelCase params per the Tauri v2 rule (CLAUDE.md).
|
|
it("should get downloaded libraries from backend", async () => {
|
|
const mockLibraries = [{ id: "lib1", name: "Music", collectionType: "music" }];
|
|
(invoke as any).mockResolvedValueOnce(mockLibraries);
|
|
|
|
const libraries = await client.getDownloadedLibraries();
|
|
|
|
expect(libraries).toEqual(mockLibraries);
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_downloaded_libraries", {
|
|
handle: "test-handle-123",
|
|
});
|
|
});
|
|
|
|
it("should get downloaded items with camelCase params", async () => {
|
|
const mockResult = {
|
|
items: [{ id: "t1", name: "Track", type: "Audio" }],
|
|
totalRecordCount: 1,
|
|
};
|
|
(invoke as any).mockResolvedValueOnce(mockResult);
|
|
|
|
const result = await client.getDownloadedItems("album1", { limit: 50 });
|
|
|
|
expect(result).toEqual(mockResult);
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_downloaded_items", {
|
|
handle: "test-handle-123",
|
|
parentId: "album1",
|
|
options: { limit: 50 },
|
|
});
|
|
});
|
|
|
|
it("should get download disk usage from backend", async () => {
|
|
const mockUsage = {
|
|
sizes: { t1: 1000 },
|
|
partialContainers: {},
|
|
deviceTotalBytes: 1000,
|
|
itemCount: 1,
|
|
};
|
|
(invoke as any).mockResolvedValueOnce(mockUsage);
|
|
|
|
const usage = await client.getDownloadDiskUsage();
|
|
|
|
expect(usage).toEqual(mockUsage);
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_download_disk_usage", {
|
|
handle: "test-handle-123",
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Playback Methods", () => {
|
|
beforeEach(async () => {
|
|
(invoke as any).mockResolvedValueOnce("test-handle-123");
|
|
await client.create("https://server.com", "user1", "token123", "server1");
|
|
});
|
|
|
|
it("should get audio stream URL", async () => {
|
|
const mockUrl = "https://server.com/Audio/item123/stream.mp3?api_key=token";
|
|
(invoke as any).mockResolvedValueOnce(mockUrl);
|
|
|
|
const url = await client.getAudioStreamUrl("item123");
|
|
|
|
expect(url).toBe(mockUrl);
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_audio_stream_url", {
|
|
handle: "test-handle-123",
|
|
itemId: "item123",
|
|
});
|
|
});
|
|
|
|
it("should get video stream URL", async () => {
|
|
const mockUrl = "https://server.com/Videos/item123/stream.mp4?api_key=token";
|
|
(invoke as any).mockResolvedValueOnce(mockUrl);
|
|
|
|
const url = await client.getVideoStreamUrl("item123");
|
|
|
|
expect(url).toBe(mockUrl);
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_video_stream_url", {
|
|
handle: "test-handle-123",
|
|
itemId: "item123",
|
|
mediaSourceId: null,
|
|
audioStreamIndex: null,
|
|
});
|
|
});
|
|
|
|
/**
|
|
* There is no start-position argument: a position on the HLS playlist makes
|
|
* the server reject every segment behind it with 400, so resume and seek are
|
|
* performed by seeking the player after load (DR-181).
|
|
*/
|
|
it("should get video stream URL with options", async () => {
|
|
const mockUrl = "https://server.com/Videos/item123/stream.mp4?api_key=token";
|
|
(invoke as any).mockResolvedValueOnce(mockUrl);
|
|
|
|
const url = await client.getVideoStreamUrl("item123", "source456", 0);
|
|
|
|
expect(url).toBe(mockUrl);
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_video_stream_url", {
|
|
handle: "test-handle-123",
|
|
itemId: "item123",
|
|
mediaSourceId: "source456",
|
|
audioStreamIndex: 0,
|
|
});
|
|
});
|
|
|
|
it("should get audio-only stream URL for a video item (camelCase params)", async () => {
|
|
// TRACES: UR-040 | JA-032 | UT-061
|
|
const mockUrl = "https://server.com/Audio/item123/universal?AudioStreamIndex=2";
|
|
(invoke as any).mockResolvedValueOnce(mockUrl);
|
|
|
|
const url = await client.getAudioOnlyStreamUrlForVideo("item123", "source456", 193, 2);
|
|
|
|
expect(url).toBe(mockUrl);
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_audio_only_stream_url_for_video", {
|
|
handle: "test-handle-123",
|
|
itemId: "item123",
|
|
mediaSourceId: "source456",
|
|
startTimeSeconds: 193,
|
|
audioStreamIndex: 2,
|
|
});
|
|
});
|
|
|
|
it("should default optional params to null for audio-only stream URL", async () => {
|
|
// TRACES: UR-040 | JA-032 | UT-061
|
|
(invoke as any).mockResolvedValueOnce("https://server.com/Audio/item123/universal");
|
|
|
|
await client.getAudioOnlyStreamUrlForVideo("item123");
|
|
|
|
expect(invoke).toHaveBeenCalledWith("repository_get_audio_only_stream_url_for_video", {
|
|
handle: "test-handle-123",
|
|
itemId: "item123",
|
|
mediaSourceId: null,
|
|
startTimeSeconds: null,
|
|
audioStreamIndex: null,
|
|
});
|
|
});
|
|
|
|
it("should report playback progress", async () => {
|
|
(invoke as any).mockResolvedValueOnce(undefined);
|
|
|
|
await client.reportPlaybackProgress("item123", 5000000);
|
|
|
|
expect(invoke).toHaveBeenCalledWith("repository_report_playback_progress", {
|
|
handle: "test-handle-123",
|
|
itemId: "item123",
|
|
positionMs: 5000000,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Playlist Methods", () => {
|
|
beforeEach(async () => {
|
|
(invoke as any).mockResolvedValueOnce("test-handle-123");
|
|
await client.create("https://server.com", "user1", "token123", "server1");
|
|
});
|
|
|
|
it("should create a playlist", async () => {
|
|
const mockResult = { id: "playlist-001" };
|
|
(invoke as any).mockResolvedValueOnce(mockResult);
|
|
|
|
const result = await client.createPlaylist("My Playlist", ["track1", "track2"]);
|
|
|
|
expect(result).toEqual(mockResult);
|
|
expect(invoke).toHaveBeenCalledWith("playlist_create", {
|
|
handle: "test-handle-123",
|
|
name: "My Playlist",
|
|
itemIds: ["track1", "track2"],
|
|
});
|
|
});
|
|
|
|
it("should create a playlist without initial items", async () => {
|
|
const mockResult = { id: "playlist-002" };
|
|
(invoke as any).mockResolvedValueOnce(mockResult);
|
|
|
|
await client.createPlaylist("Empty Playlist");
|
|
|
|
expect(invoke).toHaveBeenCalledWith("playlist_create", {
|
|
handle: "test-handle-123",
|
|
name: "Empty Playlist",
|
|
itemIds: null,
|
|
});
|
|
});
|
|
|
|
it("should delete a playlist", async () => {
|
|
(invoke as any).mockResolvedValueOnce(undefined);
|
|
|
|
await client.deletePlaylist("playlist-001");
|
|
|
|
expect(invoke).toHaveBeenCalledWith("playlist_delete", {
|
|
handle: "test-handle-123",
|
|
playlistId: "playlist-001",
|
|
});
|
|
});
|
|
|
|
it("should rename a playlist", async () => {
|
|
(invoke as any).mockResolvedValueOnce(undefined);
|
|
|
|
await client.renamePlaylist("playlist-001", "New Name");
|
|
|
|
expect(invoke).toHaveBeenCalledWith("playlist_rename", {
|
|
handle: "test-handle-123",
|
|
playlistId: "playlist-001",
|
|
name: "New Name",
|
|
});
|
|
});
|
|
|
|
it("should get playlist items", async () => {
|
|
const mockItems = [
|
|
{ playlistItemId: "entry1", id: "track1", name: "Track 1", type: "Audio" },
|
|
{ playlistItemId: "entry2", id: "track2", name: "Track 2", type: "Audio" },
|
|
];
|
|
(invoke as any).mockResolvedValueOnce(mockItems);
|
|
|
|
const items = await client.getPlaylistItems("playlist-001");
|
|
|
|
expect(items).toEqual(mockItems);
|
|
expect(invoke).toHaveBeenCalledWith("playlist_get_items", {
|
|
handle: "test-handle-123",
|
|
playlistId: "playlist-001",
|
|
});
|
|
});
|
|
|
|
it("should add items to a playlist", async () => {
|
|
(invoke as any).mockResolvedValueOnce(undefined);
|
|
|
|
await client.addToPlaylist("playlist-001", ["track3", "track4"]);
|
|
|
|
expect(invoke).toHaveBeenCalledWith("playlist_add_items", {
|
|
handle: "test-handle-123",
|
|
playlistId: "playlist-001",
|
|
itemIds: ["track3", "track4"],
|
|
});
|
|
});
|
|
|
|
it("should remove items from a playlist using entry IDs", async () => {
|
|
(invoke as any).mockResolvedValueOnce(undefined);
|
|
|
|
await client.removeFromPlaylist("playlist-001", ["entry1", "entry2"]);
|
|
|
|
expect(invoke).toHaveBeenCalledWith("playlist_remove_items", {
|
|
handle: "test-handle-123",
|
|
playlistId: "playlist-001",
|
|
entryIds: ["entry1", "entry2"],
|
|
});
|
|
});
|
|
|
|
it("should move a playlist item", async () => {
|
|
(invoke as any).mockResolvedValueOnce(undefined);
|
|
|
|
await client.movePlaylistItem("playlist-001", "track1", 3);
|
|
|
|
expect(invoke).toHaveBeenCalledWith("playlist_move_item", {
|
|
handle: "test-handle-123",
|
|
playlistId: "playlist-001",
|
|
itemId: "track1",
|
|
newIndex: 3,
|
|
});
|
|
});
|
|
|
|
it("should throw error if not initialized before playlist operations", async () => {
|
|
const newClient = new RepositoryClient();
|
|
await expect(newClient.getPlaylistItems("pl-1")).rejects.toThrow(
|
|
"Repository not initialized",
|
|
);
|
|
await expect(newClient.createPlaylist("test")).rejects.toThrow("Repository not initialized");
|
|
await expect(newClient.deletePlaylist("pl-1")).rejects.toThrow("Repository not initialized");
|
|
});
|
|
});
|
|
|
|
describe("Error Handling", () => {
|
|
it("should throw error if invoke fails", async () => {
|
|
(invoke as any).mockRejectedValueOnce(new Error("Network error"));
|
|
|
|
await expect(
|
|
client.create("https://server.com", "user1", "token", "server1"),
|
|
).rejects.toThrow("Network error");
|
|
});
|
|
|
|
it("should handle missing optional parameters", async () => {
|
|
(invoke as any).mockResolvedValueOnce("test-handle-123");
|
|
await client.create("https://server.com", "user1", "token123", "server1");
|
|
|
|
(invoke as any).mockResolvedValueOnce("");
|
|
|
|
await client.getImageUrl("item123");
|
|
|
|
expect(invoke).toHaveBeenCalledWith(
|
|
"repository_get_image_url",
|
|
expect.objectContaining({
|
|
options: null,
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
});
|