- Add GitHub Actions CI workflow with lint, type-check, test, build, and e2e jobs - Configure Jest for unit and integration tests with coverage reporting - Create unit tests for BaseAgent, PlantLineageAgent, and AgentOrchestrator - Add blockchain PlantChain unit tests - Create API integration tests for plants endpoints - Configure Cypress for E2E testing with support files and custom commands - Add E2E tests for home, plant registration, transparency, and vertical farm pages - Set up Prettier for code formatting with configuration - Configure Husky pre-commit hooks with lint-staged - Add commitlint for conventional commit message enforcement - Update package.json with new scripts and dev dependencies This implements Agent 5 (Testing & CI/CD) from the deployment plan.
169 lines
4.7 KiB
TypeScript
169 lines
4.7 KiB
TypeScript
/**
|
|
* 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>): 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);
|
|
});
|
|
});
|
|
});
|