Skip to content

Commit aa95364

Browse files
author
ddd999
committed
More testing and linting fixes
1 parent 014a2a2 commit aa95364

File tree

3 files changed

+102
-43
lines changed

3 files changed

+102
-43
lines changed

server/index.test.js

Lines changed: 62 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,23 @@ const app = require('./index');
88

99
let server;
1010

11-
// Start the server before all tests
12-
before(function(done) {
13-
// The app should export the Express app, not start the server directly
14-
server = app.listen(3001, () => {
15-
console.log('Test server started on port 3001');
16-
done();
11+
describe('Express server', function () {
12+
// Start the server before all tests
13+
before(function(done) {
14+
// The app should export the Express app, not start the server directly
15+
server = app.listen(3001, () => {
16+
console.log('Test server started on port 3001');
17+
done();
18+
});
1719
});
18-
});
1920

20-
// Close the server after all tests
21-
after(function(done) {
22-
server.close(() => {
23-
console.log('Test server closed');
24-
done();
21+
// Close the server after all tests
22+
after(function(done) {
23+
server.close(() => {
24+
console.log('Test server closed');
25+
done();
26+
});
2527
});
26-
});
2728

2829
// Helper function to make requests using Node's built-in http
2930
function request(method, path, body, headers = {}) {
@@ -38,19 +39,19 @@ function request(method, path, body, headers = {}) {
3839
...headers
3940
}
4041
};
41-
42+
4243
if (body) {
4344
const bodyData = JSON.stringify(body);
4445
options.headers['Content-Type'] = 'application/json';
4546
options.headers['Content-Length'] = Buffer.byteLength(bodyData);
4647
}
47-
48+
4849
const req = http.request(options, (res) => {
4950
let data = '';
5051
res.on('data', (chunk) => {
5152
data += chunk;
5253
});
53-
54+
5455
res.on('end', () => {
5556
res.body = data;
5657
try {
@@ -61,43 +62,79 @@ function request(method, path, body, headers = {}) {
6162
resolve(res);
6263
});
6364
});
64-
65+
6566
req.on('error', (error) => {
6667
reject(error);
6768
});
68-
69+
6970
if (body) {
7071
req.write(JSON.stringify(body));
7172
}
7273
req.end();
7374
});
7475
}
7576

76-
describe('Express server', function () {
7777
// Test the GET / endpoint
7878
describe('GET /', function () {
7979
it('should return a 200 status code', async function () {
8080
const response = await request('GET', '/');
81-
assert.equal(response.statusCode, 200); // Changed from status to statusCode
81+
assert.equal(response.statusCode, 200);
8282
});
8383
});
8484

8585
// Test that unauthorized users cannot access the /users endpoint
8686
describe('GET /users nonauth', function () {
8787
it('should return a 401 status code', async function () {
8888
const response = await request('GET', '/api/users');
89-
assert.equal(response.statusCode, 401); // Changed from status to statusCode
89+
assert.equal(response.statusCode, 401);
9090
});
9191
});
9292

9393
// Test that login works
9494
describe('POST /login', function () {
9595
it('should return a 200 status code', async function () {
96-
const response = await request('POST', '/api/login', {
97-
username: 'admin',
98-
password: 'admin'
96+
const response = await request('POST', '/api/login', {
97+
username: 'admin',
98+
password: 'admin'
9999
});
100-
assert.equal(response.statusCode, 200); // Changed from status to statusCode
100+
assert.equal(response.statusCode, 200);
101+
});
102+
});
103+
104+
// Test /api/capturestillphoto for camera not active or not photo mode
105+
describe('POST /api/capturestillphoto', function () {
106+
it('should return a 400 if camera is not active or not in photo mode', async function () {
107+
// No auth token provided, will be rejected as 401
108+
const response = await request('POST', '/api/capturestillphoto');
109+
// Accept either 400 or 401 (depends if middleware or logic triggers first)
110+
assert([400, 401].includes(response.statusCode));
111+
});
112+
});
113+
114+
// Test /api/camera/start validation error=
115+
describe('POST /api/camera/start validation', function () {
116+
it('should return 422 for missing required fields', async function () {
117+
const loginRes = await request('POST', '/api/login', {
118+
username: 'admin',
119+
password: 'admin'
120+
});
121+
const token = (loginRes.json && loginRes.json.token) || '';
122+
123+
const response = await request(
124+
'POST',
125+
'/api/camera/start',
126+
{ cameraMode: 'streaming' },
127+
{ Authorization: 'Bearer ' + token }
128+
);
129+
assert.equal(response.statusCode, 422);
130+
});
131+
});
132+
133+
// Test that /api/camera/stop returns 401 without authorization
134+
describe('POST /api/camera/stop', function () {
135+
it('should return 401 without authentication', async function () {
136+
const response = await request('POST', '/api/camera/stop');
137+
assert.equal(response.statusCode, 401);
101138
});
102139
});
103140
});

server/videostream.js

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -152,15 +152,12 @@ class videoStream {
152152

153153
// video streaming
154154
getVideoDevices(callback) {
155-
156-
process.stdout.write('\n--- TRACE: getVideoDevices called ---\n');
157155
this.winston.info("Retrieving video devices");
158156

159157
const networkInterfaces = this.scanInterfaces();
160158

161159
exec('python3 ./python/gstcaps.py', (error, stdout, stderr) => {
162160
// Initialize a safe, default response object at the beginning to ensure all callbacks receive a valid object.
163-
process.stdout.write('--- TRACE: exec callback entered ---\n');
164161
const responseData = {
165162
devices: [],
166163
networkInterfaces: networkInterfaces,
@@ -186,18 +183,13 @@ getVideoDevices(callback) {
186183
const warnstrings = ['DeprecationWarning', 'gst_element_message_full_with_details', 'camera_manager.cpp', 'Unsupported V4L2 pixel format'];
187184
if (stderr && !warnstrings.some(wrn => stderr.includes(wrn))) {
188185
this.winston.info('Error in getVideoDevices() ', { message: stderr });
189-
process.stdout.write('--- TRACE: exec had script error, calling back ---\n');
190186
return callback(stderr, responseData);
191187
}
192188

193189
try {
194-
process.stdout.write('--- TRACE: ENTERING TRY BLOCK ---\n');
195190
const devices = JSON.parse(stdout);
196191

197-
process.stdout.write('--- TRACE: JSON parsed successfully ---\n');
198-
199192
if (!Array.isArray(devices) || devices.length === 0) {
200-
process.stdout.write('--- TRACE: No devices found, calling back ---\n');
201193
return callback('No video devices found', responseData);
202194
}
203195
this.videoDevices = devices;
@@ -227,7 +219,6 @@ getVideoDevices(callback) {
227219
}
228220

229221
if (!selectedDevice || !selectedCap) {
230-
process.stdout.write('--- TRACE: No valid caps found, calling back ---\n');
231222
return callback('No valid video capabilities found on any device', responseData);
232223
}
233224

@@ -260,20 +251,15 @@ getVideoDevices(callback) {
260251
responseData.selectedFps = fpsMax > 0 ? fpsMax : (fpsOpts[0]?.value ?? 30);
261252
}
262253

263-
process.stdout.write('--- TRACE: Success! Calling final callback. ---\n');
264254
return callback(null, responseData);
265255

266256
} catch (e) {
267-
process.stdout.write('\n--- !!! TRACE: CRITICAL ERROR IN CATCH BLOCK !!! ---\n');
268-
process.stdout.write('Error Message: ' + e.message + '\n');
269-
process.stdout.write('Error Stack: ' + e.stack + '\n');
270257
this.winston.info('Error processing video devices:', { message: e.message, stack: e.stack });
271258
return callback('Failed to process video device information', responseData);
272259
}
273260
});
274261
}
275262

276-
// ... the rest of the file ...
277263
resetAndReturnDefaults(callback, defaultDevice, defaultCap, defaultFps) {
278264
this.resetCamera()
279265
return callback(null, this.videoDevices, false, defaultDevice, defaultCap,
@@ -497,7 +483,7 @@ startCamera(callback) {
497483
}
498484

499485
const args = ['./python/photomode.py', '--mode=photo']
500-
// Don't add a capture device path unless there actually is one.
486+
// Don't add a capture device path unless there actually is one.
501487
// Device path only used for V4L2 devices.
502488
if (this.stillSettings.device) {
503489
args.push('--device=' + this.stillSettings.device);
@@ -721,7 +707,6 @@ startCamera(callback) {
721707
this.deviceStream.kill('SIGUSR1')
722708

723709
//TODO: add MAVLink control option
724-
725710
}
726711

727712
sendCameraInformation (senderSysId, senderCompId, targetComponent) {
@@ -798,7 +783,6 @@ startCamera(callback) {
798783
msg.uri = this.deviceAddresses.find(addr => addr.includes(`://${this.videoSettings.mavStreamSelected}:`)
799784
);
800785
}
801-
//msg.uri='rtsp://uri'
802786

803787
// 1 = VIDEO_STREAM_STATUS_FLAGS_RUNNING
804788
// 2 = VIDEO_STREAM_STATUS_FLAGS_THERMAL

server/videostream.test.js

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ describe('Video Functions (CI Safe)', function () {
146146
});
147147
});
148148

149-
describe('startCamera() routing', function () {
149+
describe('#startCamera()', function () {
150150
it('should call startVideoStreaming for "streaming" mode', function () {
151151
const vManager = new VideoStream(settings, winston);
152152
vManager.cameraMode = 'streaming';
@@ -302,5 +302,43 @@ describe('Video Functions (CI Safe)', function () {
302302
const isNotUbuntu = await vManager.isUbuntu();
303303
assert.strictEqual(isNotUbuntu, false);
304304
});
305+
306+
it('should reset all camera settings (resetCamera)', function () {
307+
settings.setValue('camera.active', true)
308+
settings.setValue('camera.mode', 'photo')
309+
settings.setValue('camera.videoSettings', { foo: 'bar' })
310+
settings.setValue('camera.stillSettings', { foo: 'bar' })
311+
settings.setValue('camera.useHeartbeat', true)
312+
const vManager = new VideoStream(settings, winston)
313+
vManager.active = true
314+
vManager.stillSettings = { foo: 'bar' }
315+
vManager.videoSettings = { foo: 'bar' }
316+
vManager.resetCamera()
317+
assert.equal(vManager.active, false)
318+
assert.equal(vManager.videoSettings, null)
319+
assert.equal(vManager.stillSettings, null)
320+
})
321+
322+
it('should populate RTSP addresses', function () {
323+
const vManager = new VideoStream(settings, winston)
324+
sandbox.stub(vManager, 'scanInterfaces').returns(['192.168.1.1', '10.0.0.1'])
325+
vManager.populateAddresses('testfactory')
326+
assert.ok(vManager.deviceAddresses.some(addr => addr.includes('rtsp://')))
327+
})
328+
329+
it('scanInterfaces returns an array', function () {
330+
const vManager = new VideoStream(settings, winston)
331+
const ifaces = vManager.scanInterfaces()
332+
assert.ok(Array.isArray(ifaces))
333+
})
334+
335+
it('startHeartbeatInterval emits cameraheartbeat', function (done) {
336+
const vManager = new VideoStream(settings, winston)
337+
vManager.eventEmitter.on('cameraheartbeat', () => {
338+
clearInterval(vManager.intervalObj)
339+
done()
340+
})
341+
vManager.startHeartbeatInterval()
342+
})
305343
});
306344
});

0 commit comments

Comments
 (0)