UNPKG

@huggingface/hub

Version:

Utilities to interact with the Hugging Face hub

455 lines (397 loc) 18.5 kB
import { describe, expect, it, beforeEach } from "vitest"; import { SplicedBlob } from "./SplicedBlob"; describe("SplicedBlob", () => { let originalBlob: Blob; let insertBlob: Blob; let replaceBlob: Blob; beforeEach(() => { // originalBlob: "0123456789" (10 chars) originalBlob = new Blob(["0123456789"]); // insertBlob: "ABC" (3 chars) - used in tests where we insert something into the blob insertBlob = new Blob(["ABC"]); // replaceBlob: "XY" (2 chars) - used in tests where part of the blob is replaced replaceBlob = new Blob(["XY"]); }); describe("create", () => { it("should create a SplicedBlob with valid parameters", () => { const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); expect(splicedBlob).toBeInstanceOf(SplicedBlob); }); it("should throw error for negative start", () => { expect(() => SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: -1, end: 5 }])).toThrow( "Invalid start/end positions for SplicedBlob" ); }); it("should throw error for negative end", () => { expect(() => SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: -1 }])).toThrow( "Invalid start/end positions for SplicedBlob" ); }); it("should throw error for start > original.size", () => { expect(() => SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 15, end: 5 }])).toThrow( "Invalid start/end positions for SplicedBlob" ); }); it("should throw error for end > original.size", () => { expect(() => SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 15 }])).toThrow( "Invalid start/end positions for SplicedBlob" ); }); it("should throw error for start > end", () => { expect(() => SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 7, end: 5 }])).toThrow( "Invalid start/end positions for SplicedBlob" ); }); }); describe("size and type", () => { it("should calculate size correctly for insertion", () => { // Insert "ABC" at position 5: "01234ABC56789" = 13 chars const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); expect(splicedBlob.size).toBe(13); }); it("should calculate size correctly for replacement", () => { // Replace "345" with "XY": "012XY6789" = 9 chars const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: replaceBlob, start: 3, end: 6 }]); expect(splicedBlob.size).toBe(9); }); it("should return original blob type", () => { const typedBlob = new Blob(["test"], { type: "text/plain" }); const splicedBlob = SplicedBlob.create(typedBlob, [{ insert: insertBlob, start: 2, end: 2 }]); expect(splicedBlob.type).toBe("text/plain"); }); }); describe("text method", () => { it("should insert at beginning", async () => { // Insert "ABC" at start: "ABC0123456789" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 0, end: 0 }]); const text = await splicedBlob.text(); expect(text).toBe("ABC0123456789"); }); it("should insert at end", async () => { // Insert "ABC" at end: "0123456789ABC" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 10, end: 10 }]); const text = await splicedBlob.text(); expect(text).toBe("0123456789ABC"); }); it("should insert in middle", async () => { // Insert "ABC" at position 5: "01234ABC56789" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const text = await splicedBlob.text(); expect(text).toBe("01234ABC56789"); }); it("should replace content", async () => { // Replace "345" with "XY": "012XY6789" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: replaceBlob, start: 3, end: 6 }]); const text = await splicedBlob.text(); expect(text).toBe("012XY6789"); }); it("should replace everything", async () => { // Replace entire content with "ABC": "ABC" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 0, end: 10 }]); const text = await splicedBlob.text(); expect(text).toBe("ABC"); }); }); describe("slice method - basic cases", () => { it("should return empty blob for start >= end", async () => { const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(5, 5); expect(slice.size).toBe(0); expect(await slice.text()).toBe(""); }); it("should handle slice beyond size", async () => { const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(10, 20); const text = await slice.text(); expect(text).toBe("789"); // Only gets what's available }); it("should throw error for negative start/end", () => { const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); expect(() => splicedBlob.slice(-1, 5)).toThrow("Unsupported negative start/end on SplicedBlob.slice"); expect(() => splicedBlob.slice(0, -1)).toThrow("Unsupported negative start/end on SplicedBlob.slice"); }); }); describe("slice method - before segment only", () => { it("should slice entirely in before segment", async () => { // SplicedBlob: "01234ABC56789", slice(1, 4) = "123" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(1, 4); const text = await slice.text(); expect(text).toBe("123"); }); it("should slice from start of before segment", async () => { // SplicedBlob: "01234ABC56789", slice(0, 3) = "012" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(0, 3); const text = await slice.text(); expect(text).toBe("012"); }); it("should slice to end of before segment", async () => { // SplicedBlob: "01234ABC56789", slice(2, 5) = "234" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(2, 5); const text = await slice.text(); expect(text).toBe("234"); }); }); describe("slice method - insert segment only", () => { it("should slice entirely in insert segment", async () => { // SplicedBlob: "01234ABC56789", slice(6, 7) = "B" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(6, 7); const text = await slice.text(); expect(text).toBe("B"); }); it("should slice entire insert segment", async () => { // SplicedBlob: "01234ABC56789", slice(5, 8) = "ABC" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(5, 8); const text = await slice.text(); expect(text).toBe("ABC"); }); it("should slice from start of insert segment", async () => { // SplicedBlob: "01234ABC56789", slice(5, 7) = "AB" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(5, 7); const text = await slice.text(); expect(text).toBe("AB"); }); it("should slice to end of insert segment", async () => { // SplicedBlob: "01234ABC56789", slice(6, 8) = "BC" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(6, 8); const text = await slice.text(); expect(text).toBe("BC"); }); }); describe("slice method - after segment only", () => { it("should slice entirely in after segment", async () => { // SplicedBlob: "01234ABC56789", slice(9, 12) = "678" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(9, 12); const text = await slice.text(); expect(text).toBe("678"); }); it("should slice from start of after segment", async () => { // SplicedBlob: "01234ABC56789", slice(8, 11) = "567" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(8, 11); const text = await slice.text(); expect(text).toBe("567"); }); it("should slice to end of after segment", async () => { // SplicedBlob: "01234ABC56789", slice(10, 13) = "789" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(10, 13); const text = await slice.text(); expect(text).toBe("789"); }); }); describe("slice method - spanning before and insert", () => { it("should slice spanning before and insert segments", async () => { // SplicedBlob: "01234ABC56789", slice(3, 7) = "34AB" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(3, 7); const text = await slice.text(); expect(text).toBe("34AB"); }); it("should slice from start spanning before and insert", async () => { // SplicedBlob: "01234ABC56789", slice(0, 6) = "01234A" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(0, 6); const text = await slice.text(); expect(text).toBe("01234A"); }); it("should slice to end of insert spanning before and insert", async () => { // SplicedBlob: "01234ABC56789", slice(4, 8) = "4ABC" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(4, 8); const text = await slice.text(); expect(text).toBe("4ABC"); }); }); describe("slice method - spanning insert and after", () => { it("should slice spanning insert and after segments", async () => { // SplicedBlob: "01234ABC56789", slice(6, 10) = "BC56" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(6, 10); const text = await slice.text(); expect(text).toBe("BC56"); }); it("should slice from start of insert to end", async () => { // SplicedBlob: "01234ABC56789", slice(5, 13) = "ABC56789" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(5, 13); const text = await slice.text(); expect(text).toBe("ABC56789"); }); it("should slice from middle of insert spanning to after", async () => { // SplicedBlob: "01234ABC56789", slice(7, 11) = "C567" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(7, 11); const text = await slice.text(); expect(text).toBe("C567"); }); }); describe("slice method - spanning all three segments", () => { it("should slice spanning all three segments", async () => { // SplicedBlob: "01234ABC56789", slice(3, 10) = "34ABC56" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(3, 10); const text = await slice.text(); expect(text).toBe("34ABC56"); }); it("should slice entire spliced blob", async () => { // SplicedBlob: "01234ABC56789", slice(0, 13) = "01234ABC56789" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(0, 13); const text = await slice.text(); expect(text).toBe("01234ABC56789"); }); it("should slice most of spliced blob", async () => { // SplicedBlob: "01234ABC56789", slice(1, 12) = "1234ABC5678" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice = splicedBlob.slice(1, 12); const text = await slice.text(); expect(text).toBe("1234ABC5678"); }); }); describe("slice method - with replacement", () => { it("should slice before replacement", async () => { // Replace "345" with "XY": "012XY6789", slice(0, 3) = "012" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: replaceBlob, start: 3, end: 6 }]); const slice = splicedBlob.slice(0, 3); const text = await slice.text(); expect(text).toBe("012"); }); it("should slice replacement only", async () => { // Replace "345" with "XY": "012XY6789", slice(3, 5) = "XY" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: replaceBlob, start: 3, end: 6 }]); const slice = splicedBlob.slice(3, 5); const text = await slice.text(); expect(text).toBe("XY"); }); it("should slice after replacement", async () => { // Replace "345" with "XY": "012XY6789", slice(5, 9) = "6789" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: replaceBlob, start: 3, end: 6 }]); const slice = splicedBlob.slice(5, 9); const text = await slice.text(); expect(text).toBe("6789"); }); it("should slice spanning before and replacement", async () => { // Replace "345" with "XY": "012XY6789", slice(1, 4) = "12X" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: replaceBlob, start: 3, end: 6 }]); const slice = splicedBlob.slice(1, 4); const text = await slice.text(); expect(text).toBe("12X"); }); it("should slice spanning replacement and after", async () => { // Replace "345" with "XY": "012XY6789", slice(4, 7) = "Y67" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: replaceBlob, start: 3, end: 6 }]); const slice = splicedBlob.slice(4, 7); const text = await slice.text(); expect(text).toBe("Y67"); }); it("should slice spanning all segments with replacement", async () => { // Replace "345" with "XY": "012XY6789", slice(1, 8) = "12XY678" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: replaceBlob, start: 3, end: 6 }]); const slice = splicedBlob.slice(1, 8); const text = await slice.text(); expect(text).toBe("12XY678"); }); }); describe("slice method - edge cases", () => { it("should handle empty insert blob", async () => { const emptyBlob = new Blob([""]); // Replace "345" with "": "0126789" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: emptyBlob, start: 3, end: 6 }]); const text = await splicedBlob.text(); expect(text).toBe("0126789"); const slice = splicedBlob.slice(2, 5); expect(await slice.text()).toBe("267"); }); it("should handle slice at segment boundaries", async () => { // SplicedBlob: "01234ABC56789", exact boundary slices const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); // End of before segment expect(await splicedBlob.slice(4, 5).text()).toBe("4"); // Start of insert segment expect(await splicedBlob.slice(5, 6).text()).toBe("A"); // End of insert segment expect(await splicedBlob.slice(7, 8).text()).toBe("C"); // Start of after segment expect(await splicedBlob.slice(8, 9).text()).toBe("5"); }); it("should handle insert at beginning with slice", async () => { // Insert "ABC" at start: "ABC0123456789" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 0, end: 0 }]); const slice = splicedBlob.slice(1, 5); expect(await slice.text()).toBe("BC01"); }); it("should handle insert at end with slice", async () => { // Insert "ABC" at end: "0123456789ABC" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 10, end: 10 }]); const slice = splicedBlob.slice(8, 12); expect(await slice.text()).toBe("89AB"); }); }); describe("arrayBuffer method", () => { it("should return correct ArrayBuffer for insertion", async () => { const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const buffer = await splicedBlob.arrayBuffer(); const text = new TextDecoder().decode(buffer); expect(text).toBe("01234ABC56789"); expect(buffer.byteLength).toBe(13); }); it("should return correct ArrayBuffer for replacement", async () => { const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: replaceBlob, start: 3, end: 6 }]); const buffer = await splicedBlob.arrayBuffer(); const text = new TextDecoder().decode(buffer); expect(text).toBe("012XY6789"); expect(buffer.byteLength).toBe(9); }); }); describe("stream method", () => { it("should return correct stream for insertion", async () => { const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const stream = splicedBlob.stream(); const response = new Response(stream); const text = await response.text(); expect(text).toBe("01234ABC56789"); }); it("should return correct stream for replacement", async () => { const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: replaceBlob, start: 3, end: 6 }]); const stream = splicedBlob.stream(); const response = new Response(stream); const text = await response.text(); expect(text).toBe("012XY6789"); }); it("should handle empty segments in stream", async () => { const emptyBlob = new Blob([""]); const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: emptyBlob, start: 5, end: 5 }]); const stream = splicedBlob.stream(); const response = new Response(stream); const text = await response.text(); expect(text).toBe("0123456789"); }); }); describe("nested slicing", () => { it("should allow slicing of sliced blob", async () => { // SplicedBlob: "01234ABC56789", slice(3, 10) = "34ABC56", then slice(2, 5) = "ABC" const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const firstSlice = splicedBlob.slice(3, 10); const secondSlice = firstSlice.slice(2, 5); const text = await secondSlice.text(); expect(text).toBe("ABC"); }); it("should handle multiple levels of slicing", async () => { // Complex slicing chain const splicedBlob = SplicedBlob.create(originalBlob, [{ insert: insertBlob, start: 5, end: 5 }]); const slice1 = splicedBlob.slice(2, 11); // "234ABC567" const slice2 = slice1.slice(1, 7); // "34ABC5" const slice3 = slice2.slice(2, 5); // "ABC" const text = await slice3.text(); expect(text).toBe("ABC"); }); }); });