'use strict'; /** * Integration tests – obstacle controller (controllers/obstacle.js) * * Covers: createObstacle_post, updateObstacle_put, deleteObstacle, near_post */ const { connectDB, disconnectDB, clearCollection } = require('./jest.setup'); const { mockReq, mockRes, newId } = require('./mock_data'); let Obstacle; beforeAll(async () => { await connectDB(); Obstacle = require('../../model/obstacles'); }); afterAll(async () => { await disconnectDB(); }); const obstacleCtl = require('../../controllers/obstacle'); describe('obstacle controller – data methods', () => { afterAll(async () => { await clearCollection(Obstacle); }); // ------------------------------------------------------------------------- describe('createObstacle_post', () => { it('creates an obstacle and returns it as a plain object', async () => { const userId = newId(); const req = mockReq({ uid: userId, body: { item: { name: 'Test Tower', type: 'USER', agl: 150, amsl: 500, coors: [-80.1234, 25.7617], }, pUser: String(userId), }, }); const res = mockRes(); await obstacleCtl.createObstacle_post(req, res); expect(res.json).toHaveBeenCalled(); const obstacle = res._data; expect(obstacle._id).toBeDefined(); expect(obstacle.name).toBe('Test Tower'); expect(obstacle.agl).toBe(150); expect(obstacle.amsl).toBe(500); }); it('throws AppParamError when body is missing', async () => { const req = mockReq({ body: null }); const res = mockRes(); await expect(obstacleCtl.createObstacle_post(req, res)).rejects.toThrow(); }); it('throws AppParamError when coordinates are missing', async () => { const req = mockReq({ body: { item: { name: 'Bad Obs', agl: 100, coors: [] }, pUser: String(newId()), }, }); const res = mockRes(); await expect(obstacleCtl.createObstacle_post(req, res)).rejects.toThrow(); }); }); // ------------------------------------------------------------------------- describe('updateObstacle_put', () => { let obstacleId; beforeAll(async () => { const obs = await Obstacle.create({ properties: { name: 'Original', type: 'USER', agl: 100, amsl: 300 }, geometry: { type: 'Point', coordinates: [-80.0, 25.0] }, }); obstacleId = String(obs._id); }); it('updates name and agl and returns updated obstacle', async () => { const req = mockReq({ params: { ob_id: obstacleId }, body: { name: 'Updated Tower', agl: 200, amsl: 400 }, }); const res = mockRes(); await obstacleCtl.updateObstacle_put(req, res); expect(res.json).toHaveBeenCalled(); expect(res._data).toBeTruthy(); expect(res._data.name).toBe('Updated Tower'); expect(res._data.agl).toBe(200); }); }); // ------------------------------------------------------------------------- describe('deleteObstacle', () => { let obstacleId; beforeAll(async () => { const obs = await Obstacle.create({ properties: { name: 'To Delete', type: 'USER', agl: 50, amsl: 200 }, geometry: { type: 'Point', coordinates: [-79.0, 26.0] }, }); obstacleId = String(obs._id); }); it('deletes obstacle and returns success message', async () => { const req = mockReq({ params: { ob_id: obstacleId } }); const res = mockRes(); await obstacleCtl.deleteObstacle(req, res); expect(res.json).toHaveBeenCalled(); expect(res._data.message).toMatch(/deleted/i); const found = await Obstacle.findById(obstacleId); expect(found).toBeNull(); }); it('returns success even when obstacle does not exist', async () => { const req = mockReq({ params: { ob_id: String(newId()) } }); const res = mockRes(); await obstacleCtl.deleteObstacle(req, res); expect(res.json).toHaveBeenCalled(); expect(res._data.message).toMatch(/deleted/i); }); }); // ------------------------------------------------------------------------- describe('near_post', () => { beforeAll(async () => { await Obstacle.ensureIndexes(); await Obstacle.create({ properties: { name: 'Miami Tower', type: 'USER', agl: 200, amsl: 600 }, geometry: { type: 'Point', coordinates: [-80.1918, 25.7617] }, }); }); it('returns obstacles near the given center within radius', async () => { const req = mockReq({ body: { center: { lng: -80.1918, lat: 25.7617 }, min: 0 }, }); const res = mockRes(); await obstacleCtl.near_post(req, res); expect(res.json).toHaveBeenCalled(); expect(Array.isArray(res._data)).toBe(true); expect(res._data.length).toBeGreaterThan(0); }); it('returns empty array when no obstacles are in the vicinity', async () => { const req = mockReq({ body: { center: { lng: -140.0, lat: 0.0 }, min: 0 }, }); const res = mockRes(); await obstacleCtl.near_post(req, res); expect(res.json).toHaveBeenCalled(); expect(res._data.length).toBe(0); }); it('filters by minimum AGL height', async () => { const req = mockReq({ body: { center: { lng: -80.1918, lat: 25.7617 }, min: 999 }, }); const res = mockRes(); await obstacleCtl.near_post(req, res); expect(res.json).toHaveBeenCalled(); expect(res._data.length).toBe(0); }); }); });