188 lines
5.5 KiB
JavaScript
188 lines
5.5 KiB
JavaScript
'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);
|
||
});
|
||
});
|
||
});
|