import { NDKCashuToken } from "@nostr-dev-kit/ndk"; import NDK from "@nostr-dev-kit/ndk"; import { mockProof } from "@nostr-dev-kit/ndk/test"; import { beforeEach, describe, expect, it } from "vitest"; import type { WalletProofChange, WalletState } from "."; import { NDKCashuWallet } from ".."; import { calculateNewState } from "./update"; const ndk = new NDK(); describe("calculateNewState", () => { const wallet = new NDKCashuWallet(ndk); let walletState: WalletState; let stateChange: WalletProofChange; beforeEach(() => { walletState = wallet.state; stateChange = { store: [], destroy: [], reserve: [], mint: "mintId", }; }); it("should handle empty state change", () => { const result = calculateNewState(walletState, stateChange); expect(result).toEqual({ deletedTokenIds: new Set(), deletedProofs: new Set(), reserveProofs: [], saveProofs: [], }); }); it("should handle storing new proofs", () => { const newProof = mockProof("proof1", 100); stateChange.store = [newProof]; const result = calculateNewState(walletState, stateChange); expect(result.saveProofs).toContain(newProof); expect(result.deletedTokenIds.size).toBe(0); }); it("should handle destroying proofs and affected tokens", () => { const existingTokenId = "token1"; const existingProof = mockProof("proof1", 100); const existingToken = new NDKCashuToken(ndk); existingToken.mint = "mint"; existingToken.proofs = [existingProof]; existingToken.id = existingTokenId; walletState.addToken(existingToken); stateChange.destroy = [existingProof]; const result = calculateNewState(walletState, stateChange); expect(result.deletedTokenIds).toContain(existingTokenId); expect(result.deletedProofs).toContain(existingProof.C); expect(result.saveProofs).not.toContain(existingProof); }); it("should handle moving unused proofs from a deleted token to a new token", () => { const existingTokenId = "token1"; const usedProof = mockProof("proof1", 100); const unusedProof = mockProof("proof2", 100); const existingToken = new NDKCashuToken(ndk); existingToken.mint = "mint"; existingToken.id = existingTokenId; existingToken.proofs = [usedProof, unusedProof]; walletState.addToken(existingToken); stateChange.destroy = [usedProof]; const result = calculateNewState(walletState, stateChange); expect(result.deletedTokenIds).toContain(existingTokenId); expect(result.saveProofs).toContainEqual(unusedProof); expect(result.saveProofs).not.toContain(usedProof); }); it("should handle destroying multiple proofs from multiple tokens, moving unused proofs from multiple tokens", () => { const tokens = [new NDKCashuToken(ndk), new NDKCashuToken(ndk)]; tokens.forEach((token, index) => { token.mint = "mint"; token.id = `token${index}`; token.proofs = [mockProof(`proof-${index}-1`, 100), mockProof(`proof-${index}-2`, 100)]; walletState.addToken(token); }); stateChange.store = []; stateChange.destroy = [tokens[0].proofs[0], tokens[1].proofs[0]]; const result = calculateNewState(walletState, stateChange); expect(result.deletedTokenIds).toContain(tokens[0].id); expect(result.deletedTokenIds).toContain(tokens[1].id); expect(result.saveProofs).toContain(tokens[0].proofs[1]); expect(result.saveProofs).toContain(tokens[1].proofs[1]); expect(result.saveProofs).not.toContain(tokens[0].proofs[0]); expect(result.saveProofs).not.toContain(tokens[1].proofs[0]); }); it("should throw an error when reserve is used", () => { stateChange.reserve = [mockProof("proof3", 300)]; expect(() => calculateNewState(walletState, stateChange)).not.toThrow(); // Note: The updateInternalState function throws an error on reserve, // but calculateNewState does not handle reserve directly. }); });