// Mock CouchDB service for testing const mockCouchdbService = { createDocument: jest.fn(), findDocumentById: jest.fn(), updateDocument: jest.fn(), findByType: jest.fn(), initialize: jest.fn(), find: jest.fn(), findStreetsByLocation: jest.fn(), }; // Mock the service module jest.mock('../../services/couchdbService', () => mockCouchdbService); const Street = require('../../models/Street'); const User = require('../../models/User'); describe('Street Model', () => { beforeEach(() => { jest.clearAllMocks(); // Reset all mocks to ensure clean state mockCouchdbService.createDocument.mockReset(); mockCouchdbService.findDocumentById.mockReset(); mockCouchdbService.updateDocument.mockReset(); mockCouchdbService.findByType.mockReset(); mockCouchdbService.initialize.mockReset(); mockCouchdbService.find.mockReset(); mockCouchdbService.findStreetsByLocation.mockReset(); }); describe('Schema Validation', () => { it('should create a valid street', async () => { const streetData = { name: 'Main Street', location: { type: 'Point', coordinates: [-73.935242, 40.730610], }, city: 'New York', state: 'NY', }; const mockCreated = { _id: 'street_123', _rev: '1-abc', type: 'street', ...streetData, status: 'available', adoptedBy: null, createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.initialize.mockResolvedValue(true); mockCouchdbService.createDocument.mockResolvedValue(mockCreated); const street = await Street.create(streetData); expect(street._id).toBeDefined(); expect(street.name).toBe(streetData.name); expect(street.location.type).toBe('Point'); expect(street.location.coordinates).toEqual(streetData.location.coordinates); expect(street.status).toBe('available'); }); it('should require name field', async () => { const streetData = { location: { type: 'Point', coordinates: [-73.935242, 40.730610], }, city: 'New York', state: 'NY', }; expect(() => new Street(streetData)).toThrow(); }); it('should require location field', async () => { const streetData = { name: 'Main Street', city: 'New York', state: 'NY', }; expect(() => new Street(streetData)).toThrow(); }); it('should not require adoptedBy field', async () => { const streetData = { name: 'Main Street', location: { type: 'Point', coordinates: [-73.935242, 40.730610], }, city: 'New York', state: 'NY', }; const mockCreated = { _id: 'street_123', _rev: '1-abc', type: 'street', ...streetData, status: 'available', adoptedBy: null, createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.createDocument.mockResolvedValue(mockCreated); const street = await Street.create(streetData); expect(street._id).toBeDefined(); expect(street.adoptedBy).toBeNull(); expect(street.status).toBe('available'); }); }); describe('GeoJSON Location', () => { it('should store Point type correctly', async () => { const streetData = { name: 'Geo Street', location: { type: 'Point', coordinates: [-122.4194, 37.7749], // San Francisco }, city: 'San Francisco', state: 'CA', }; const mockCreated = { _id: 'street_123', _rev: '1-abc', type: 'street', ...streetData, status: 'available', adoptedBy: null, createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.createDocument.mockResolvedValue(mockCreated); const street = await Street.create(streetData); expect(street.location.type).toBe('Point'); expect(street.location.coordinates).toEqual([-122.4194, 37.7749]); expect(street.location.coordinates[0]).toBe(-122.4194); // longitude expect(street.location.coordinates[1]).toBe(37.7749); // latitude }); it('should support geospatial queries', async () => { const streetData = { name: 'NYC Street', location: { type: 'Point', coordinates: [-73.935242, 40.730610], }, city: 'New York', state: 'NY', }; const mockCreated = { _id: 'street_123', _rev: '1-abc', type: 'street', ...streetData, status: 'available', adoptedBy: null, createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.createDocument.mockResolvedValue(mockCreated); mockCouchdbService.findByType.mockResolvedValue([mockCreated]); mockCouchdbService.findStreetsByLocation.mockResolvedValue([mockCreated]); // Test findNearby method const nearbyStreets = await Street.findNearby([-73.935242, 40.730610], 1000); expect(nearbyStreets.length).toBeGreaterThan(0); expect(nearbyStreets[0].name).toBe('NYC Street'); }); }); describe('Status Field', () => { it('should default status to available', async () => { const streetData = { name: 'Status Street', location: { type: 'Point', coordinates: [-73.935242, 40.730610], }, city: 'New York', state: 'NY', }; const mockCreated = { _id: 'street_123', _rev: '1-abc', type: 'street', ...streetData, status: 'available', adoptedBy: null, createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.createDocument.mockResolvedValue(mockCreated); const street = await Street.create(streetData); expect(street.status).toBe('available'); }); it('should allow setting custom status', async () => { const streetData = { name: 'Custom Status Street', location: { type: 'Point', coordinates: [-73.935242, 40.730610], }, city: 'New York', state: 'NY', status: 'adopted', }; const mockCreated = { _id: 'street_123', _rev: '1-abc', type: 'street', ...streetData, adoptedBy: null, createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.createDocument.mockResolvedValue(mockCreated); const street = await Street.create(streetData); expect(street.status).toBe('adopted'); }); }); describe('Timestamps', () => { it('should automatically set createdAt and updatedAt', async () => { const streetData = { name: 'Timestamp Street', location: { type: 'Point', coordinates: [-73.935242, 40.730610], }, city: 'New York', state: 'NY', }; const mockCreated = { _id: 'street_123', _rev: '1-abc', type: 'street', ...streetData, status: 'available', adoptedBy: null, createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.createDocument.mockResolvedValue(mockCreated); const street = await Street.create(streetData); expect(street.createdAt).toBeDefined(); expect(street.updatedAt).toBeDefined(); expect(typeof street.createdAt).toBe('string'); expect(typeof street.updatedAt).toBe('string'); }); }); describe('Relationships', () => { it('should reference User model through adoptedBy', async () => { const streetData = { name: 'Relationship Street', location: { type: 'Point', coordinates: [-73.935242, 40.730610], }, city: 'New York', state: 'NY', adoptedBy: { userId: 'user_123', name: 'Adopter User', profilePicture: '' }, status: 'adopted', }; const mockCreated = { _id: 'street_123', _rev: '1-abc', type: 'street', ...streetData, createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.createDocument.mockResolvedValue(mockCreated); const street = await Street.create(streetData); expect(street.adoptedBy).toBeDefined(); expect(street.adoptedBy.name).toBe('Adopter User'); }); }); describe('Coordinates Format', () => { it('should accept valid longitude and latitude', async () => { const validCoordinates = [ [-180, -90], // min values [180, 90], // max values [0, 0], // origin [-74.006, 40.7128], // NYC ]; for (const coords of validCoordinates) { const streetData = { name: `Street at ${coords.join(',')}`, location: { type: 'Point', coordinates: coords, }, city: 'Test City', state: 'TS', }; const mockCreated = { _id: 'street_123', _rev: '1-abc', type: 'street', ...streetData, status: 'available', adoptedBy: null, createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.createDocument.mockResolvedValue(mockCreated); const street = await Street.create(streetData); expect(street.location.coordinates).toEqual(coords); } }); }); });