diff --git a/contracts/erc721/src/test/erc721.test.ts b/contracts/erc721/src/test/erc721.test.ts index 27f84114..80f62e71 100644 --- a/contracts/erc721/src/test/erc721.test.ts +++ b/contracts/erc721/src/test/erc721.test.ts @@ -53,6 +53,26 @@ describe('ERC721', () => { expect(token.name()).toEqual(EMPTY_STRING); expect(token.symbol()).toEqual(EMPTY_STRING); }); + + it('should initialize metadata with whitespace', () => { + token = new ERC721Simulator(' NAME ', ' SYMBOL '); + expect(token.name()).toEqual(' NAME '); + expect(token.symbol()).toEqual(' SYMBOL '); + }); + + it('should initialize metadata with special characters', () => { + token = new ERC721Simulator('NAME!@#', 'SYMBOL$%^'); + expect(token.name()).toEqual('NAME!@#'); + expect(token.symbol()).toEqual('SYMBOL$%^'); + }); + + it('should initialize metadata with very long strings', () => { + const longName = 'A'.repeat(1000); + const longSymbol = 'B'.repeat(1000); + token = new ERC721Simulator(longName, longSymbol); + expect(token.name()).toEqual(longName); + expect(token.symbol()).toEqual(longSymbol); + }); }); beforeEach(() => { @@ -68,6 +88,32 @@ describe('ERC721', () => { token._mint(_Z_OWNER, _AMOUNT); expect(token.balanceOf(_Z_OWNER)).toEqual(_AMOUNT); }); + + it('should return correct balance for multiple tokens', () => { + token._mint(_Z_OWNER, TOKENID); + token._mint(_Z_OWNER, TOKENID + 1n); + token._mint(_Z_OWNER, TOKENID + 2n); + expect(token.balanceOf(_Z_OWNER)).toEqual(3n); + }); + + it('should return correct balance after burning multiple tokens', () => { + token._mint(_Z_OWNER, TOKENID); + token._mint(_Z_OWNER, TOKENID + 1n); + token._mint(_Z_OWNER, TOKENID + 2n); + token._burn(TOKENID); + token._burn(TOKENID + 1n); + expect(token.balanceOf(_Z_OWNER)).toEqual(1n); + }); + + it('should return correct balance after transferring multiple tokens', () => { + token._mint(_Z_OWNER, TOKENID); + token._mint(_Z_OWNER, TOKENID + 1n); + token._mint(_Z_OWNER, TOKENID + 2n); + token._transfer(_Z_OWNER, _Z_SPENDER, TOKENID); + token._transfer(_Z_OWNER, _Z_SPENDER, TOKENID + 1n); + expect(token.balanceOf(_Z_OWNER)).toEqual(1n); + expect(token.balanceOf(_Z_SPENDER)).toEqual(2n); + }); }); describe('ownerOf', () => { @@ -89,6 +135,31 @@ describe('ERC721', () => { token._mint(_Z_OWNER, TOKENID); expect(token.ownerOf(TOKENID)).toEqual(_Z_OWNER); }); + + it('should return correct owner for multiple tokens', () => { + token._mint(_Z_OWNER, TOKENID); + token._mint(_Z_OWNER, TOKENID + 1n); + token._mint(_Z_OWNER, TOKENID + 2n); + expect(token.ownerOf(TOKENID)).toEqual(_Z_OWNER); + expect(token.ownerOf(TOKENID + 1n)).toEqual(_Z_OWNER); + expect(token.ownerOf(TOKENID + 2n)).toEqual(_Z_OWNER); + }); + + it('should return correct owner after multiple transfers', () => { + token._mint(_Z_OWNER, TOKENID); + token._mint(_Z_OWNER, TOKENID + 1n); + token._transfer(_Z_OWNER, _Z_SPENDER, TOKENID); + token._transfer(_Z_OWNER, _Z_OTHER, TOKENID + 1n); + expect(token.ownerOf(TOKENID)).toEqual(_Z_SPENDER); + expect(token.ownerOf(TOKENID + 1n)).toEqual(_Z_OTHER); + }); + + it('should return correct owner after multiple burns and mints', () => { + token._mint(_Z_OWNER, TOKENID); + token._burn(TOKENID); + token._mint(_Z_SPENDER, TOKENID); + expect(token.ownerOf(TOKENID)).toEqual(_Z_SPENDER); + }); }); describe('tokenURI', () => { @@ -109,6 +180,41 @@ describe('ERC721', () => { token._setTokenURI(TOKENID, SOME_STRING); expect(token.tokenURI(TOKENID)).toEqual(SOME_STRING); }); + + it('should return empty string tokenURI', () => { + token._mint(_Z_OWNER, TOKENID); + token._setTokenURI(TOKENID, ''); + expect(token.tokenURI(TOKENID)).toEqual(''); + }); + + it('should return very long tokenURI', () => { + const longURI = 'A'.repeat(1000); + token._mint(_Z_OWNER, TOKENID); + token._setTokenURI(TOKENID, longURI); + expect(token.tokenURI(TOKENID)).toEqual(longURI); + }); + + it('should return tokenURI with special characters', () => { + const specialURI = '!@#$%^&*()_+'; + token._mint(_Z_OWNER, TOKENID); + token._setTokenURI(TOKENID, specialURI); + expect(token.tokenURI(TOKENID)).toEqual(specialURI); + }); + + it('should update tokenURI multiple times', () => { + token._mint(_Z_OWNER, TOKENID); + token._setTokenURI(TOKENID, 'URI1'); + token._setTokenURI(TOKENID, 'URI2'); + token._setTokenURI(TOKENID, 'URI3'); + expect(token.tokenURI(TOKENID)).toEqual('URI3'); + }); + + it('should maintain tokenURI after token transfer', () => { + token._mint(_Z_OWNER, TOKENID); + token._setTokenURI(TOKENID, SOME_STRING); + token._transfer(_Z_OWNER, _Z_SPENDER, TOKENID); + expect(token.tokenURI(TOKENID)).toEqual(SOME_STRING); + }); }); describe('approve', () => { @@ -146,6 +252,37 @@ describe('ERC721', () => { token.approve(_Z_OTHER, TOKENID, _caller); }).toThrow('ERC721: Invalid Approver'); }); + + it('should approve same address multiple times', () => { + _caller = _OWNER; + token.approve(_Z_SPENDER, TOKENID, _caller); + token.approve(_Z_SPENDER, TOKENID, _caller); + expect(token.getApproved(TOKENID)).toEqual(_Z_SPENDER); + }); + + it('should approve after token transfer', () => { + _caller = _OWNER; + token._transfer(_Z_OWNER, _Z_SPENDER, TOKENID); + _caller = _SPENDER; + token.approve(_Z_OTHER, TOKENID, _caller); + expect(token.getApproved(TOKENID)).toEqual(_Z_OTHER); + }); + + it('should approve after token burn and remint', () => { + _caller = _OWNER; + token._burn(TOKENID); + token._mint(_Z_OWNER, TOKENID); + token.approve(_Z_SPENDER, TOKENID, _caller); + expect(token.getApproved(TOKENID)).toEqual(_Z_SPENDER); + }); + + it('should approve with very long token ID', () => { + _caller = _OWNER; + const longTokenId = BigInt('18446744073709551615'); + token._mint(_Z_OWNER, longTokenId); + token.approve(_Z_SPENDER, longTokenId, _caller); + expect(token.getApproved(longTokenId)).toEqual(_Z_SPENDER); + }); }); describe('getApproved', () => { @@ -223,6 +360,40 @@ describe('ERC721', () => { token.approve(_Z_SPENDER, TOKENID, _caller); }).toThrow('ERC721: Invalid Approver'); }); + + it('should set approval for all to same address multiple times', () => { + _caller = _OWNER; + token._mint(_Z_OWNER, TOKENID); + token.setApprovalForAll(_Z_SPENDER, true, _caller); + token.setApprovalForAll(_Z_SPENDER, true, _caller); + expect(token.isApprovedForAll(_Z_OWNER, _Z_SPENDER)).toBe(true); + }); + + it('should set approval for all after token transfer', () => { + _caller = _OWNER; + token._mint(_Z_OWNER, TOKENID); + token._transfer(_Z_OWNER, _Z_SPENDER, TOKENID); + _caller = _SPENDER; + token.setApprovalForAll(_Z_OTHER, true, _caller); + expect(token.isApprovedForAll(_Z_SPENDER, _Z_OTHER)).toBe(true); + }); + + it('should set approval for all with multiple operators', () => { + _caller = _OWNER; + token._mint(_Z_OWNER, TOKENID); + token.setApprovalForAll(_Z_SPENDER, true, _caller); + token.setApprovalForAll(_Z_OTHER, true, _caller); + expect(token.isApprovedForAll(_Z_OWNER, _Z_SPENDER)).toBe(true); + expect(token.isApprovedForAll(_Z_OWNER, _Z_OTHER)).toBe(true); + }); + + it('should set approval for all with very long token IDs', () => { + _caller = _OWNER; + const longTokenId = BigInt('18446744073709551615'); + token._mint(_Z_OWNER, longTokenId); + token.setApprovalForAll(_Z_SPENDER, true, _caller); + expect(token.isApprovedForAll(_Z_OWNER, _Z_SPENDER)).toBe(true); + }); }); describe('isApprovedForAll', () => { @@ -294,6 +465,76 @@ describe('ERC721', () => { token.transferFrom(_Z_OWNER, _Z_SPENDER, TOKENID, _caller); expect(token.ownerOf(TOKENID)).toEqual(_Z_SPENDER); }); + + it('should allow transfer to same address', () => { + _caller = _OWNER; + token._mint(_Z_OWNER, TOKENID); + expect(() => { + token.transferFrom(_Z_OWNER, _Z_OWNER, TOKENID, _caller); + }).not.toThrow(); + expect(token.ownerOf(TOKENID)).toEqual(_Z_OWNER); + expect(token.balanceOf(_Z_OWNER)).toEqual(1n); + }); + + it('should not transfer after approval revocation', () => { + _caller = _OWNER; + token._mint(_Z_OWNER, TOKENID); + token.approve(_Z_SPENDER, TOKENID, _caller); + token.approve(ZERO_KEY, TOKENID, _caller); + _caller = _SPENDER; + expect(() => { + token.transferFrom(_Z_OWNER, _Z_SPENDER, TOKENID, _caller); + }).toThrow('ERC721: Insufficient Approval'); + }); + + it('should not transfer after approval for all revocation', () => { + _caller = _OWNER; + token._mint(_Z_OWNER, TOKENID); + token.setApprovalForAll(_Z_SPENDER, true, _caller); + token.setApprovalForAll(_Z_SPENDER, false, _caller); + _caller = _SPENDER; + expect(() => { + token.transferFrom(_Z_OWNER, _Z_SPENDER, TOKENID, _caller); + }).toThrow('ERC721: Insufficient Approval'); + }); + + it('should transfer multiple tokens in sequence', () => { + _caller = _OWNER; + token._mint(_Z_OWNER, TOKENID); + token._mint(_Z_OWNER, TOKENID + 1n); + token._mint(_Z_OWNER, TOKENID + 2n); + token.approve(_Z_SPENDER, TOKENID, _caller); + token.approve(_Z_SPENDER, TOKENID + 1n, _caller); + token.approve(_Z_SPENDER, TOKENID + 2n, _caller); + + _caller = _SPENDER; + token.transferFrom(_Z_OWNER, _Z_SPENDER, TOKENID, _caller); + token.transferFrom(_Z_OWNER, _Z_SPENDER, TOKENID + 1n, _caller); + token.transferFrom(_Z_OWNER, _Z_SPENDER, TOKENID + 2n, _caller); + + expect(token.ownerOf(TOKENID)).toEqual(_Z_SPENDER); + expect(token.ownerOf(TOKENID + 1n)).toEqual(_Z_SPENDER); + expect(token.ownerOf(TOKENID + 2n)).toEqual(_Z_SPENDER); + }); + + it('should transfer with very long token IDs', () => { + _caller = _OWNER; + const longTokenId = BigInt('18446744073709551615'); + token._mint(_Z_OWNER, longTokenId); + token.approve(_Z_SPENDER, longTokenId, _caller); + + _caller = _SPENDER; + token.transferFrom(_Z_OWNER, _Z_SPENDER, longTokenId, _caller); + expect(token.ownerOf(longTokenId)).toEqual(_Z_SPENDER); + }); + + it('should revoke approval after transferFrom', () => { + _caller = _OWNER; + token._mint(_Z_OWNER, TOKENID); + token.approve(_Z_SPENDER, TOKENID, _caller); + token.transferFrom(_Z_OWNER, _Z_OTHER, TOKENID, _caller); + expect(token.getApproved(TOKENID)).toEqual(ZERO_KEY); + }); }); describe('_requireOwned', () => { @@ -388,6 +629,53 @@ describe('ERC721', () => { token._update(_Z_SPENDER, TOKENID, _Z_SPENDER); }).toThrow('ERC721: Insufficient Approval'); }); + + it('should update multiple tokens in sequence', () => { + _caller = _OWNER; + token._mint(_Z_OWNER, TOKENID); + token._mint(_Z_OWNER, TOKENID + 1n); + token._mint(_Z_OWNER, TOKENID + 2n); + token.approve(_Z_SPENDER, TOKENID, _caller); + token.approve(_Z_SPENDER, TOKENID + 1n, _caller); + token.approve(_Z_SPENDER, TOKENID + 2n, _caller); + + token._update(_Z_SPENDER, TOKENID, _Z_SPENDER); + token._update(_Z_SPENDER, TOKENID + 1n, _Z_SPENDER); + token._update(_Z_SPENDER, TOKENID + 2n, _Z_SPENDER); + + expect(token.ownerOf(TOKENID)).toEqual(_Z_SPENDER); + expect(token.ownerOf(TOKENID + 1n)).toEqual(_Z_SPENDER); + expect(token.ownerOf(TOKENID + 2n)).toEqual(_Z_SPENDER); + }); + + it('should update with very long token IDs', () => { + _caller = _OWNER; + const longTokenId = BigInt('18446744073709551615'); + token._mint(_Z_OWNER, longTokenId); + token.approve(_Z_SPENDER, longTokenId, _caller); + token._update(_Z_SPENDER, longTokenId, _Z_SPENDER); + expect(token.ownerOf(longTokenId)).toEqual(_Z_SPENDER); + }); + + it('should update after multiple transfers', () => { + _caller = _OWNER; + token._mint(_Z_OWNER, TOKENID); + token._transfer(_Z_OWNER, _Z_SPENDER, TOKENID); + _caller = _SPENDER; + token.approve(_Z_OTHER, TOKENID, _caller); + token._update(_Z_OTHER, TOKENID, _Z_OTHER); + expect(token.ownerOf(TOKENID)).toEqual(_Z_OTHER); + }); + + it('should update after multiple burns', () => { + _caller = _OWNER; + token._mint(_Z_OWNER, TOKENID); + token._burn(TOKENID); + token._mint(_Z_OWNER, TOKENID); + token.approve(_Z_SPENDER, TOKENID, _caller); + token._update(_Z_SPENDER, TOKENID, _Z_SPENDER); + expect(token.ownerOf(TOKENID)).toEqual(_Z_SPENDER); + }); }); describe('_approve', () => { @@ -525,6 +813,32 @@ describe('ERC721', () => { token._mint(_Z_OWNER, TOKENID + 2n); expect(token.balanceOf(_Z_OWNER)).toEqual(3n); }); + + it('should mint multiple tokens in sequence', () => { + for (let i = 0; i < 10; i++) { + token._mint(_Z_OWNER, TOKENID + BigInt(i)); + } + expect(token.balanceOf(_Z_OWNER)).toEqual(10n); + }); + + it('should mint with very long token IDs', () => { + const longTokenId = BigInt('18446744073709551615'); + token._mint(_Z_OWNER, longTokenId); + expect(token.ownerOf(longTokenId)).toEqual(_Z_OWNER); + }); + + it('should mint after burning', () => { + token._mint(_Z_OWNER, TOKENID); + token._burn(TOKENID); + token._mint(_Z_OWNER, TOKENID); + expect(token.ownerOf(TOKENID)).toEqual(_Z_OWNER); + }); + + it('should mint with special characters in metadata', () => { + token._mint(_Z_OWNER, TOKENID); + token._setTokenURI(TOKENID, '!@#$%^&*()_+'); + expect(token.tokenURI(TOKENID)).toEqual('!@#$%^&*()_+'); + }); }); describe('_burn', () => { @@ -552,6 +866,39 @@ describe('ERC721', () => { token._burn(TOKENID); expect(token._getApproved(TOKENID)).toEqual(ZERO_KEY); }); + + it('should burn multiple tokens in sequence', () => { + token._mint(_Z_OWNER, TOKENID); + token._mint(_Z_OWNER, TOKENID + 1n); + token._mint(_Z_OWNER, TOKENID + 2n); + token._burn(TOKENID); + token._burn(TOKENID + 1n); + token._burn(TOKENID + 2n); + expect(token.balanceOf(_Z_OWNER)).toEqual(0n); + }); + + it('should burn with very long token IDs', () => { + const longTokenId = BigInt('18446744073709551615'); + token._mint(_Z_OWNER, longTokenId); + token._burn(longTokenId); + expect(token._ownerOf(longTokenId)).toEqual(ZERO_KEY); + }); + + it('should burn after transfer', () => { + token._mint(_Z_OWNER, TOKENID); + token._transfer(_Z_OWNER, _Z_SPENDER, TOKENID); + token._burn(TOKENID); + expect(token._ownerOf(TOKENID)).toEqual(ZERO_KEY); + }); + + it('should burn after approval', () => { + _caller = _OWNER; + token._mint(_Z_OWNER, TOKENID); + token.approve(_Z_SPENDER, TOKENID, _caller); + token._burn(TOKENID); + expect(token._ownerOf(TOKENID)).toEqual(ZERO_KEY); + expect(token._getApproved(TOKENID)).toEqual(ZERO_KEY); + }); }); describe('_transfer', () => { @@ -592,6 +939,14 @@ describe('ERC721', () => { token._transfer(_Z_OWNER, _Z_SPENDER, TOKENID); }).toThrow('ERC721: Nonexistent Token'); }); + + it('should revoke approval after _transfer', () => { + _caller = _OWNER; + token._mint(_Z_OWNER, TOKENID); + token.approve(_Z_SPENDER, TOKENID, _caller); + token._transfer(_Z_OWNER, _Z_OTHER, TOKENID, _caller); + expect(token.getApproved(TOKENID)).toEqual(ZERO_KEY); + }); }); describe('_setTokenURI', () => { @@ -677,6 +1032,14 @@ describe('ERC721', () => { token._unsafe_transfer(_Z_OWNER, _Z_SPENDER, TOKENID); }).toThrow('ERC721: Nonexistent Token'); }); + + it('should revoke approval after _unsafe_transfer', () => { + _caller = _OWNER; + token._mint(_Z_OWNER, TOKENID); + token.approve(_Z_SPENDER, TOKENID, _caller); + token._unsafe_transfer(_Z_OWNER, _Z_OTHER, TOKENID); + expect(token.getApproved(TOKENID)).toEqual(ZERO_KEY); + }); }); describe('_unsafeTransferFrom', () => { @@ -735,5 +1098,13 @@ describe('ERC721', () => { token._unsafeTransferFrom(_Z_OWNER, _Z_SPENDER, TOKENID, _caller); expect(token.ownerOf(TOKENID)).toEqual(_Z_SPENDER); }); + + it('should revoke approval after _unsafeTransferFrom', () => { + _caller = _OWNER; + token._mint(_Z_OWNER, TOKENID); + token.approve(_Z_SPENDER, TOKENID, _caller); + token._unsafeTransferFrom(_Z_OWNER, _Z_OTHER, TOKENID, _caller); + expect(token.getApproved(TOKENID)).toEqual(ZERO_KEY); + }); }); });