/** * Carbon Calculation Tests * Tests for carbon footprint calculation logic */ import { TransportChain } from '../../../lib/transport/tracker'; import { CARBON_FACTORS, TransportMethod, TransportLocation, } from '../../../lib/transport/types'; describe('Carbon Footprint Calculation', () => { let chain: TransportChain; beforeEach(() => { chain = new TransportChain(1); }); describe('CARBON_FACTORS', () => { it('should have zero emissions for walking', () => { expect(CARBON_FACTORS.walking).toBe(0); }); it('should have zero emissions for bicycle', () => { expect(CARBON_FACTORS.bicycle).toBe(0); }); it('should have low emissions for electric vehicles', () => { expect(CARBON_FACTORS.electric_vehicle).toBeLessThan(CARBON_FACTORS.gasoline_vehicle); expect(CARBON_FACTORS.electric_truck).toBeLessThan(CARBON_FACTORS.diesel_truck); }); it('should have high emissions for air transport', () => { expect(CARBON_FACTORS.air).toBeGreaterThan(CARBON_FACTORS.diesel_truck); expect(CARBON_FACTORS.air).toBeGreaterThan(CARBON_FACTORS.ship); }); it('should have moderate emissions for refrigerated trucks', () => { expect(CARBON_FACTORS.refrigerated_truck).toBeGreaterThan(CARBON_FACTORS.diesel_truck); }); it('should have lowest emissions for rail and ship', () => { expect(CARBON_FACTORS.rail).toBeLessThan(CARBON_FACTORS.diesel_truck); expect(CARBON_FACTORS.ship).toBeLessThan(CARBON_FACTORS.diesel_truck); }); it('should define all transport methods', () => { const methods: TransportMethod[] = [ 'walking', 'bicycle', 'electric_vehicle', 'hybrid_vehicle', 'gasoline_vehicle', 'diesel_truck', 'electric_truck', 'refrigerated_truck', 'rail', 'ship', 'air', 'drone', 'local_delivery', 'customer_pickup' ]; methods.forEach(method => { expect(CARBON_FACTORS[method]).toBeDefined(); expect(typeof CARBON_FACTORS[method]).toBe('number'); }); }); }); describe('calculateCarbon', () => { it('should calculate zero carbon for zero distance', () => { const carbon = chain.calculateCarbon('diesel_truck', 0, 10); expect(carbon).toBe(0); }); it('should calculate zero carbon for zero weight', () => { const carbon = chain.calculateCarbon('diesel_truck', 100, 0); expect(carbon).toBe(0); }); it('should calculate zero carbon for walking', () => { const carbon = chain.calculateCarbon('walking', 10, 5); expect(carbon).toBe(0); }); it('should calculate correct carbon for known values', () => { // diesel_truck = 0.15 kg CO2 per km per kg cargo const carbon = chain.calculateCarbon('diesel_truck', 100, 10); expect(carbon).toBe(0.15 * 100 * 10); }); it('should increase linearly with distance', () => { const carbon1 = chain.calculateCarbon('diesel_truck', 50, 10); const carbon2 = chain.calculateCarbon('diesel_truck', 100, 10); expect(carbon2).toBe(carbon1 * 2); }); it('should increase linearly with weight', () => { const carbon1 = chain.calculateCarbon('diesel_truck', 100, 5); const carbon2 = chain.calculateCarbon('diesel_truck', 100, 10); expect(carbon2).toBe(carbon1 * 2); }); it('should compare transport methods correctly', () => { const distance = 100; const weight = 10; const walking = chain.calculateCarbon('walking', distance, weight); const electric = chain.calculateCarbon('electric_vehicle', distance, weight); const gasoline = chain.calculateCarbon('gasoline_vehicle', distance, weight); const diesel = chain.calculateCarbon('diesel_truck', distance, weight); const air = chain.calculateCarbon('air', distance, weight); expect(walking).toBe(0); expect(electric).toBeLessThan(gasoline); expect(gasoline).toBeLessThan(diesel); expect(diesel).toBeLessThan(air); }); }); describe('Distance Calculation', () => { it('should calculate zero distance for same location', () => { const location: TransportLocation = { latitude: 40.7128, longitude: -74.006, locationType: 'farm', }; const distance = TransportChain.calculateDistance(location, location); expect(distance).toBe(0); }); it('should calculate correct distance for known locations', () => { // New York to Los Angeles is approximately 3940 km const newYork: TransportLocation = { latitude: 40.7128, longitude: -74.006, locationType: 'warehouse', }; const losAngeles: TransportLocation = { latitude: 34.0522, longitude: -118.2437, locationType: 'warehouse', }; const distance = TransportChain.calculateDistance(newYork, losAngeles); expect(distance).toBeGreaterThan(3900); expect(distance).toBeLessThan(4000); }); it('should calculate short distances accurately', () => { // Two points 1km apart (approximately) const point1: TransportLocation = { latitude: 40.7128, longitude: -74.006, locationType: 'farm', }; const point2: TransportLocation = { latitude: 40.7218, // ~1km north longitude: -74.006, locationType: 'farm', }; const distance = TransportChain.calculateDistance(point1, point2); expect(distance).toBeGreaterThan(0.9); expect(distance).toBeLessThan(1.1); }); it('should be symmetric', () => { const pointA: TransportLocation = { latitude: 40.7128, longitude: -74.006, locationType: 'farm', }; const pointB: TransportLocation = { latitude: 34.0522, longitude: -118.2437, locationType: 'warehouse', }; const distanceAB = TransportChain.calculateDistance(pointA, pointB); const distanceBA = TransportChain.calculateDistance(pointB, pointA); expect(distanceAB).toBeCloseTo(distanceBA, 5); }); it('should handle crossing the prime meridian', () => { const london: TransportLocation = { latitude: 51.5074, longitude: -0.1278, locationType: 'warehouse', }; const paris: TransportLocation = { latitude: 48.8566, longitude: 2.3522, locationType: 'warehouse', }; const distance = TransportChain.calculateDistance(london, paris); // London to Paris is approximately 344 km expect(distance).toBeGreaterThan(330); expect(distance).toBeLessThan(360); }); it('should handle crossing the equator', () => { const north: TransportLocation = { latitude: 10, longitude: 0, locationType: 'farm', }; const south: TransportLocation = { latitude: -10, longitude: 0, locationType: 'farm', }; const distance = TransportChain.calculateDistance(north, south); // 20 degrees of latitude is approximately 2222 km expect(distance).toBeGreaterThan(2200); expect(distance).toBeLessThan(2250); }); }); describe('Carbon Tracking in Events', () => { it('should auto-calculate carbon if not provided', () => { const event = { id: 'test-carbon-auto', timestamp: new Date().toISOString(), eventType: 'seed_acquisition' as const, fromLocation: { latitude: 40, longitude: -74, locationType: 'seed_bank' as const }, toLocation: { latitude: 41, longitude: -74, locationType: 'farm' as const }, distanceKm: 100, durationMinutes: 120, transportMethod: 'diesel_truck' as const, carbonFootprintKg: 0, // Will be auto-calculated senderId: 'sender', receiverId: 'receiver', status: 'verified' as const, seedBatchId: 'batch-001', sourceType: 'seed_bank' as const, species: 'Test', quantity: 100, quantityUnit: 'seeds' as const, generation: 1, }; const block = chain.recordEvent(event); expect(block.transportEvent.carbonFootprintKg).toBeGreaterThan(0); }); it('should accumulate carbon across chain', () => { const event1 = { id: 'carbon-chain-1', timestamp: new Date().toISOString(), eventType: 'seed_acquisition' as const, fromLocation: { latitude: 40, longitude: -74, locationType: 'seed_bank' as const }, toLocation: { latitude: 41, longitude: -74, locationType: 'farm' as const }, distanceKm: 100, durationMinutes: 120, transportMethod: 'diesel_truck' as const, carbonFootprintKg: 0, senderId: 'sender', receiverId: 'receiver', status: 'verified' as const, seedBatchId: 'batch-001', sourceType: 'seed_bank' as const, species: 'Test', quantity: 100, quantityUnit: 'seeds' as const, generation: 1, }; const event2 = { ...event1, id: 'carbon-chain-2', distanceKm: 200 }; const block1 = chain.recordEvent(event1); const block2 = chain.recordEvent(event2); expect(block2.cumulativeCarbonKg).toBeGreaterThan(block1.cumulativeCarbonKg); expect(block2.cumulativeCarbonKg).toBeCloseTo( block1.cumulativeCarbonKg + block2.transportEvent.carbonFootprintKg, 5 ); }); }); describe('Environmental Impact Calculations', () => { it('should calculate carbon savings vs conventional', () => { const userId = 'eco-user'; const event = { id: 'eco-event', timestamp: new Date().toISOString(), eventType: 'seed_acquisition' as const, fromLocation: { latitude: 40, longitude: -74, locationType: 'seed_bank' as const }, toLocation: { latitude: 40.1, longitude: -74, locationType: 'farm' as const }, distanceKm: 10, // Very short local distance durationMinutes: 20, transportMethod: 'bicycle' as const, // Zero carbon carbonFootprintKg: 0, senderId: userId, receiverId: userId, status: 'verified' as const, seedBatchId: 'batch-eco', sourceType: 'seed_bank' as const, species: 'Test', quantity: 1000, quantityUnit: 'grams' as const, generation: 1, }; chain.recordEvent(event); const impact = chain.getEnvironmentalImpact(userId); // Local, zero-carbon transport should save significantly vs conventional expect(impact.comparisonToConventional.carbonSaved).toBeGreaterThan(0); expect(impact.comparisonToConventional.milesSaved).toBeGreaterThan(0); expect(impact.comparisonToConventional.percentageReduction).toBeGreaterThan(0); }); it('should break down impact by transport method', () => { const userId = 'breakdown-user'; // Record multiple events with different methods const methods: TransportMethod[] = ['bicycle', 'electric_vehicle', 'diesel_truck']; methods.forEach((method, i) => { chain.recordEvent({ id: `breakdown-${i}`, timestamp: new Date().toISOString(), eventType: 'seed_acquisition' as const, fromLocation: { latitude: 40, longitude: -74, locationType: 'seed_bank' as const }, toLocation: { latitude: 41, longitude: -74, locationType: 'farm' as const }, distanceKm: 50, durationMinutes: 60, transportMethod: method, carbonFootprintKg: 0, senderId: userId, receiverId: userId, status: 'verified' as const, seedBatchId: `batch-${i}`, sourceType: 'seed_bank' as const, species: 'Test', quantity: 100, quantityUnit: 'seeds' as const, generation: 1, }); }); const impact = chain.getEnvironmentalImpact(userId); expect(impact.breakdownByMethod['bicycle']).toBeDefined(); expect(impact.breakdownByMethod['electric_vehicle']).toBeDefined(); expect(impact.breakdownByMethod['diesel_truck']).toBeDefined(); // Bicycle should have zero carbon expect(impact.breakdownByMethod['bicycle'].carbon).toBe(0); // Diesel should have highest carbon expect(impact.breakdownByMethod['diesel_truck'].carbon) .toBeGreaterThan(impact.breakdownByMethod['electric_vehicle'].carbon); }); }); });