/** * PlantChain Tests * Tests for the blockchain implementation */ import { PlantChain } from '../../../lib/blockchain/PlantChain'; import { PlantData } from '../../../lib/blockchain/types'; describe('PlantChain', () => { let chain: PlantChain; beforeEach(() => { chain = new PlantChain(); }); describe('Initialization', () => { it('should create chain with genesis block', () => { expect(chain.getChain().length).toBe(1); }); it('should have valid genesis block', () => { const genesisBlock = chain.getChain()[0]; expect(genesisBlock.index).toBe(0); expect(genesisBlock.previousHash).toBe('0'); }); }); describe('Adding Plants', () => { const createTestPlant = (overrides?: Partial): PlantData => ({ id: `plant-${Date.now()}`, name: 'Test Plant', species: 'Test Species', variety: 'Test Variety', generation: 1, propagationType: 'original', dateAcquired: new Date().toISOString(), location: { latitude: 40.7128, longitude: -74.006, city: 'New York', }, status: 'healthy', ...overrides, }); it('should add new plant to chain', () => { const plant = createTestPlant(); chain.addPlant(plant); expect(chain.getChain().length).toBe(2); }); it('should generate valid block hash', () => { const plant = createTestPlant(); chain.addPlant(plant); const newBlock = chain.getChain()[1]; expect(newBlock.hash).toBeDefined(); expect(newBlock.hash.length).toBeGreaterThan(0); }); it('should link blocks correctly', () => { const plant = createTestPlant(); chain.addPlant(plant); const genesisBlock = chain.getChain()[0]; const newBlock = chain.getChain()[1]; expect(newBlock.previousHash).toBe(genesisBlock.hash); }); it('should store plant data correctly', () => { const plant = createTestPlant({ name: 'My Tomato' }); chain.addPlant(plant); const newBlock = chain.getChain()[1]; expect(newBlock.plant.name).toBe('My Tomato'); }); it('should add multiple plants', () => { chain.addPlant(createTestPlant({ id: 'plant-1' })); chain.addPlant(createTestPlant({ id: 'plant-2' })); chain.addPlant(createTestPlant({ id: 'plant-3' })); expect(chain.getChain().length).toBe(4); }); }); describe('Finding Plants', () => { beforeEach(() => { chain.addPlant({ id: 'tomato-1', name: 'Cherry Tomato', species: 'Tomato', variety: 'Cherry', generation: 1, propagationType: 'original', dateAcquired: new Date().toISOString(), location: { latitude: 40.7, longitude: -74.0 }, status: 'healthy', }); chain.addPlant({ id: 'basil-1', name: 'Sweet Basil', species: 'Basil', variety: 'Genovese', generation: 1, propagationType: 'seed', dateAcquired: new Date().toISOString(), location: { latitude: 40.8, longitude: -73.9 }, status: 'thriving', }); }); it('should find plant by ID', () => { const block = chain.findPlant('tomato-1'); expect(block).toBeDefined(); expect(block?.plant.name).toBe('Cherry Tomato'); }); it('should return undefined for non-existent plant', () => { const block = chain.findPlant('non-existent'); expect(block).toBeUndefined(); }); }); describe('Chain Validation', () => { it('should validate empty chain (genesis only)', () => { expect(chain.isValid()).toBe(true); }); it('should validate chain with plants', () => { chain.addPlant({ id: 'plant-1', name: 'Test Plant', species: 'Test', variety: 'Test', generation: 1, propagationType: 'original', dateAcquired: new Date().toISOString(), location: { latitude: 40.7, longitude: -74.0 }, status: 'healthy', }); expect(chain.isValid()).toBe(true); }); }); describe('Serialization', () => { beforeEach(() => { chain.addPlant({ id: 'plant-1', name: 'Test Plant', species: 'Test', variety: 'Test', generation: 1, propagationType: 'original', dateAcquired: new Date().toISOString(), location: { latitude: 40.7, longitude: -74.0 }, status: 'healthy', }); }); it('should export to JSON', () => { const json = chain.toJSON(); expect(json).toBeDefined(); expect(Array.isArray(json)).toBe(true); }); it('should import from JSON', () => { const json = chain.toJSON(); const restored = PlantChain.fromJSON(json); expect(restored.getChain().length).toBe(chain.getChain().length); }); }); });