Browse Source

Exported tests to individual files

rigwild 2 years ago
parent
commit
ef1d4a3c75

+ 1 - 1
package.json

@@ -9,7 +9,7 @@
     "app:dev": "vue-cli-service serve",
     "app:build": "vue-cli-service build",
     "app:lint": "vue-cli-service lint",
-    "test": "ava"
+    "test": "node test/api/_test_setup_start.js && ava --verbose && node test/api/_test_setup_stop.js"
   },
   "dependencies": {
     "boom": "^7.3.0",

+ 40 - 0
test/api/_test_functions.js

@@ -0,0 +1,40 @@
+'use strict'
+
+import path from 'path'
+import express from 'express'
+import serveStatic from 'serve-static'
+import routes from '../../server/routes'
+import { apiPrefix, imageServedUrl, imagesPath } from '../../config'
+
+// Path to `test` directory
+export const testDir = path.resolve(__dirname, '..')
+
+// Pretty-print a JSON object
+export const json = obj => 'JSON DATA : ' + (JSON.stringify(obj, null, 2) || obj)
+
+/**
+ * Uses supertest to open an Express server on an ephemeral port.
+ * The server serves images in `test/images`, all api routes and
+ * uses a custom error handler (no logging to stdout).
+ *
+ * Using `request` (supertest) on this object will start the server
+ *
+ * @returns {object} an Express server
+ */
+export const serve = () => {
+  const app = express()
+  app.use(imageServedUrl, serveStatic(imagesPath))
+  app.use(apiPrefix, routes)
+  app.use((err, req, res, next) => {
+    res.status(err.output.payload.statusCode).json({
+      message: err.message || err.output.payload.message,
+      data: err.data || undefined
+    })
+  })
+  return app
+}
+
+// Before each tests, start a server
+export const beforeEachTests = async t => {
+  t.context.server = serve()
+}

+ 12 - 0
test/api/_test_setup_start.js

@@ -0,0 +1,12 @@
+'use strict'
+
+const fs = require('fs-extra')
+const path = require('path')
+
+const setup = async () => {
+  const testDir = path.resolve(__dirname, '..')
+  await fs.remove(path.resolve(testDir, 'images'))
+  await fs.copy(path.resolve(testDir, 'images_test'), path.resolve(testDir, 'images'))
+}
+
+setup()

+ 11 - 0
test/api/_test_setup_stop.js

@@ -0,0 +1,11 @@
+'use strict'
+
+const fs = require('fs-extra')
+const path = require('path')
+
+const setup = async () => {
+  const testDir = path.resolve(__dirname, '..')
+  await fs.remove(path.resolve(testDir, 'images'))
+}
+
+setup()

+ 0 - 241
test/api/api.js

@@ -1,241 +0,0 @@
-import test from 'ava'
-import request from 'supertest'
-import fs from 'fs-extra'
-import path from 'path'
-import express from 'express'
-import serveStatic from 'serve-static'
-import { apiPrefix, imageServedUrl, imagesPath } from '../../config'
-import routes from '../../server/routes'
-
-// Path to `test` directory
-const testDir = path.resolve(__dirname, '..', '..', 'test')
-
-// Pretty-print a JSON object
-const json = obj => 'JSON DATA : ' + (JSON.stringify(obj, null, 2) || obj)
-
-/**
- * Uses supertest to open an Express server on an ephemeral port.
- * The server serves images in `test/images`, all api routes and
- * uses a custom error handler (no logging to stdout).
- *
- * Using `request` (supertest) on this object will start the server
- *
- * @returns {object} an Express server
- */
-const serve = () => {
-  const app = express()
-  app.use(imageServedUrl, serveStatic(imagesPath))
-  app.use(apiPrefix, routes)
-  app.use((err, req, res, next) => {
-    res.status(err.output.payload.statusCode).json({
-      message: err.message || err.output.payload.message,
-      data: err.data || undefined
-    })
-  })
-  return app
-}
-
-// Before starting all the tests, copy `test/images_test` to `test/images`
-test.before(async t => {
-  await fs.remove(path.resolve(testDir, 'images'))
-  await fs.copy(path.resolve(testDir, 'images_test'), path.resolve(testDir, 'images'))
-})
-
-// Before each tests, start a server
-test.beforeEach(async t => {
-  t.context.server = serve()
-})
-
-// After finishing all the tests, remove `test/images`
-test.after.always(async t => {
-  await fs.remove(path.resolve(testDir, 'images'))
-})
-
-
-// ROUTE /listScenes
-test('GET /listScenes', async t => {
-  const res = await request(t.context.server)
-    .get(`${apiPrefix}/listScenes`)
-
-  t.is(res.status, 200, json(res))
-  t.deepEqual(res.body.data, ['bathroom', 'contemporary'], json(res.body))
-})
-
-
-// ROUTE /listSceneQualities
-test('GET /listSceneQualities', async t => {
-  const res = await request(t.context.server)
-    .get(`${apiPrefix}/listSceneQualities`)
-
-  t.is(res.status, 400, json(res))
-  t.true(res.body.message.includes('Missing parameter'), json(res.body))
-  t.true(res.body.message.includes('sceneName'), json(res.body))
-})
-
-test('GET /listSceneQualities?sceneName=invalid/../scene', async t => {
-  const res = await request(t.context.server)
-    .get(`${apiPrefix}/listSceneQualities?sceneName=invalid/../scene`)
-
-  t.is(res.status, 409, json(res))
-  t.truthy(res.body.message.match(/The requested scene name.*is not valid/), json(res.body))
-})
-
-test('GET /listSceneQualities?sceneName=unknown-scene-name', async t => {
-  const res = await request(t.context.server)
-    .get(`${apiPrefix}/listSceneQualities?sceneName=unknown-scene-name`)
-
-  t.is(res.status, 500, json(res))
-  t.truthy(res.body.message.match(/Can't access.*scene dir.*Check it exist.*and you have read permission/), json(res.body))
-})
-
-test('GET /listSceneQualities?sceneName=bathroom', async t => {
-  const res = await request(t.context.server)
-    .get(`${apiPrefix}/listSceneQualities?sceneName=bathroom`)
-
-  t.is(res.status, 200, json(res))
-  t.true(Array.isArray(res.body.data) && res.body.data.every(x => Number.isInteger(x)), json(res.body))
-})
-
-
-// ROUTE /getImage
-test('GET /getImage', async t => {
-  const res = await request(t.context.server)
-    .get(`${apiPrefix}/getImage`)
-
-  t.is(res.status, 400, json(res))
-  t.true(res.body.message.includes('Missing parameter'), json(res.body))
-  t.true(res.body.message.includes('sceneName'), json(res.body))
-  t.true(res.body.message.includes('imageQuality'), json(res.body))
-})
-
-test('GET /getImage?sceneName=invalid/../scene&imageQuality=aaaa', async t => {
-  const res = await request(t.context.server)
-    .get(`${apiPrefix}/getImage?sceneName=invalid/../scene&imageQuality=aaaa`)
-
-  t.is(res.status, 400, json(res))
-  t.true(res.body.message.includes('Invalid query parameter'), json(res.body))
-  t.truthy(res.body.data.find(x => x.match(/The requested scene name.*is not valid/)), json(res.body))
-  t.truthy(res.body.data.find(x => x.includes('The specified quality is not an integer')), json(res.body))
-})
-
-test('GET /getImage?sceneName=unknown-scene-name&imageQuality=10', async t => {
-  const res = await request(t.context.server)
-    .get(`${apiPrefix}/getImage?sceneName=unknown-scene-name&imageQuality=10`)
-
-  t.is(res.status, 500, json(res))
-  t.truthy(res.body.message.match(/Can't access.*scene dir.*Check it exist.*and you have read permission/), json(res.body))
-})
-
-test('GET /getImage?sceneName=bathroom&imageQuality=999999', async t => {
-  const res = await request(t.context.server)
-    .get(`${apiPrefix}/getImage?sceneName=bathroom&imageQuality=999999`)
-
-  t.is(res.status, 404, json(res))
-  t.truthy(res.body.message.match(/requested quality.*not found for.*scene/), json(res.body))
-})
-
-test('GET /getImage?sceneName=bathroom&imageQuality=10', async t => {
-  const res = await request(t.context.server)
-    .get(`${apiPrefix}/getImage?sceneName=bathroom&imageQuality=10`)
-
-  t.is(res.status, 200, json(res))
-  t.is(res.body.data, `${imageServedUrl}/bathroom/bathroom_00010.png`, json(res.body))
-
-  // Check link is accessible and is an image
-  const res2 = await request(t.context.server)
-    .get(`${imageServedUrl}/bathroom/bathroom_00010.png`)
-
-  t.is(res2.status, 200, json(res2))
-  t.is(res2.header['content-type'], 'image/png', json(res2))
-})
-
-
-// ROUTE /getImageExtracts
-test('GET /getImageExtracts', async t => {
-  const res = await request(t.context.server)
-    .get(`${apiPrefix}/getImageExtracts`)
-
-  t.is(res.status, 400, json(res))
-  t.true(res.body.message.includes('Missing parameter'), json(res.body))
-  t.true(res.body.message.includes('sceneName'), json(res.body))
-  t.true(res.body.message.includes('imageQuality'), json(res.body))
-  t.true(res.body.message.includes('horizontalExtractCount'), json(res.body))
-  t.true(res.body.message.includes('verticalExtractCount'), json(res.body))
-})
-
-test('GET /getImageExtracts?sceneName=/../&imageQuality=a&horizontalExtractCount=a&verticalExtractCount=a', async t => {
-  const res = await request(t.context.server)
-    .get(`${apiPrefix}/getImageExtracts?sceneName=/../&imageQuality=a&horizontalExtractCount=a&verticalExtractCount=a`)
-
-  t.is(res.status, 400, json(res))
-  t.true(res.body.message.includes('Invalid query parameter'), json(res.body))
-  t.truthy(res.body.data.find(x => x.match(/The requested scene name.*is not valid/)), json(res.body))
-  t.truthy(res.body.data.find(x => x.includes('The specified quality is not an integer')), json(res.body))
-  t.truthy(res.body.data.find(x => x.includes('horizontal axis is not an integer')), json(res.body))
-  t.truthy(res.body.data.find(x => x.includes('vertical axis is not an integer')), json(res.body))
-})
-
-test('GET /getImageExtracts?sceneName=unknown-scene-name&imageQuality=10&horizontalExtractCount=5&verticalExtractCount=2', async t => {
-  const res = await request(t.context.server)
-    .get(`${apiPrefix}/getImageExtracts?sceneName=unknown-scene-name&imageQuality=10&horizontalExtractCount=5&verticalExtractCount=2`)
-
-  t.is(res.status, 500, json(res))
-  t.truthy(res.body.message.match(/Can't access.*scene dir.*Check it exist.*and you have read permission/), json(res.body))
-})
-
-test('GET /getImageExtracts?sceneName=bathroom&imageQuality=99999&horizontalExtractCount=5&verticalExtractCount=2', async t => {
-  const res = await request(t.context.server)
-    .get(`${apiPrefix}/getImageExtracts?sceneName=bathroom&imageQuality=99999&horizontalExtractCount=5&verticalExtractCount=2`)
-
-  t.is(res.status, 404, json(res))
-  t.truthy(res.body.message.match(/requested quality.*not found for.*scene/), json(res.body))
-})
-
-test('GET /getImageExtracts?sceneName=bathroom&imageQuality=10&horizontalExtractCount=0&verticalExtractCount=9999', async t => {
-  const res = await request(t.context.server)
-    .get(`${apiPrefix}/getImageExtracts?sceneName=bathroom&imageQuality=10&horizontalExtractCount=0&verticalExtractCount=9999`)
-
-  t.is(res.status, 400, json(res))
-  t.true(res.body.message.includes('Invalid query parameter'), json(res.body))
-  t.truthy(res.body.data.find(x => x.includes('Incompatible number of horizontal extracts')), json(res.body))
-  t.truthy(res.body.data.find(x => x.includes('Incompatible number of vertical extracts')), json(res.body))
-})
-
-test.serial('GET /getImageExtracts?sceneName=bathroom&imageQuality=10&horizontalExtractCount=5&verticalExtractCount=2', async t => {
-  const res = await request(t.context.server)
-    .get(`${apiPrefix}/getImageExtracts?sceneName=bathroom&imageQuality=10&horizontalExtractCount=5&verticalExtractCount=2`)
-
-  t.is(res.status, 200, json(res))
-  t.true(Array.isArray(res.body.data), json(res.body))
-  t.is(res.body.data[0], `${imageServedUrl}/bathroom/extracts/x5_y2/zone00001/bathroom_zone00001_10.png`, json(res.body))
-
-  // Check link is accessible and is an image
-  const res2 = await request(t.context.server)
-    .get(`${imageServedUrl}/bathroom/extracts/x5_y2/zone00001/bathroom_zone00001_10.png`)
-
-  t.is(res2.status, 200, json(res2))
-  t.is(res2.header['content-type'], 'image/png', json(res2))
-})
-
-test.serial('Extracts were successfully generated', async t => {
-  // Check the extract on the file system
-  const extracts = path.resolve(imagesPath, 'bathroom', 'extracts')
-  const aBathroomConfig = path.resolve(extracts, 'x5_y2')
-  const aBathroomConfigZone = path.resolve(aBathroomConfig, 'zone00001')
-  const aBathroomConfigZoneImg = path.resolve(aBathroomConfigZone, 'bathroom_zone00001_10.png')
-  const fsp = fs.promises
-  // Check `bathroom/extracts`
-  t.true(await fs.pathExists(extracts))
-  t.deepEqual(await fsp.readdir(extracts), ['x5_y2'])
-
-  // Check `bathroom/extracts/x5_y2`
-  t.true(await fs.pathExists(aBathroomConfig), aBathroomConfig)
-  t.is((await fsp.readdir(aBathroomConfig)).length, 10)
-
-  // Check `bathroom/extracts/x5_y2/zone00001`
-  t.true(await fs.pathExists(aBathroomConfigZone))
-  t.is((await fsp.readdir(aBathroomConfigZone)).length, 1)
-
-  // Check `bathroom/extracts/x5_y2/zone00001/bathroom_zone00001_10.png`
-  t.true(await fs.pathExists(aBathroomConfigZoneImg))
-})

+ 62 - 0
test/api/getImage.js

@@ -0,0 +1,62 @@
+'use strict'
+
+import test from 'ava'
+import request from 'supertest'
+import { apiPrefix, imageServedUrl } from '../../config'
+import { json, beforeEachTests } from './_test_functions'
+
+// ROUTE /getImage
+
+// Before each tests, start a server
+test.beforeEach(beforeEachTests)
+
+test('GET /getImage', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/getImage`)
+
+  t.is(res.status, 400, json(res))
+  t.true(res.body.message.includes('Missing parameter'), json(res.body))
+  t.true(res.body.message.includes('sceneName'), json(res.body))
+  t.true(res.body.message.includes('imageQuality'), json(res.body))
+})
+
+test('GET /getImage?sceneName=invalid/../scene&imageQuality=aaaa', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/getImage?sceneName=invalid/../scene&imageQuality=aaaa`)
+
+  t.is(res.status, 400, json(res))
+  t.true(res.body.message.includes('Invalid query parameter'), json(res.body))
+  t.truthy(res.body.data.find(x => x.match(/The requested scene name.*is not valid/)), json(res.body))
+  t.truthy(res.body.data.find(x => x.includes('The specified quality is not an integer')), json(res.body))
+})
+
+test('GET /getImage?sceneName=unknown-scene-name&imageQuality=10', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/getImage?sceneName=unknown-scene-name&imageQuality=10`)
+
+  t.is(res.status, 500, json(res))
+  t.truthy(res.body.message.match(/Can't access.*scene dir.*Check it exist.*and you have read permission/), json(res.body))
+})
+
+test('GET /getImage?sceneName=bathroom&imageQuality=999999', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/getImage?sceneName=bathroom&imageQuality=999999`)
+
+  t.is(res.status, 404, json(res))
+  t.truthy(res.body.message.match(/requested quality.*not found for.*scene/), json(res.body))
+})
+
+test('GET /getImage?sceneName=bathroom&imageQuality=10', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/getImage?sceneName=bathroom&imageQuality=10`)
+
+  t.is(res.status, 200, json(res))
+  t.is(res.body.data, `${imageServedUrl}/bathroom/bathroom_00010.png`, json(res.body))
+
+  // Check link is accessible and is an image
+  const res2 = await request(t.context.server)
+    .get(`${imageServedUrl}/bathroom/bathroom_00010.png`)
+
+  t.is(res2.status, 200, json(res2))
+  t.is(res2.header['content-type'], 'image/png', json(res2))
+})

+ 102 - 0
test/api/getImageExtracts.js

@@ -0,0 +1,102 @@
+'use strict'
+
+import test from 'ava'
+import request from 'supertest'
+import fs from 'fs-extra'
+import path from 'path'
+import { apiPrefix, imageServedUrl, imagesPath } from '../../config'
+import { json, beforeEachTests } from './_test_functions'
+
+// ROUTE /getImageExtracts
+
+// Before each tests, start a server
+test.beforeEach(beforeEachTests)
+
+test('GET /getImageExtracts', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/getImageExtracts`)
+
+  t.is(res.status, 400, json(res))
+  t.true(res.body.message.includes('Missing parameter'), json(res.body))
+  t.true(res.body.message.includes('sceneName'), json(res.body))
+  t.true(res.body.message.includes('imageQuality'), json(res.body))
+  t.true(res.body.message.includes('horizontalExtractCount'), json(res.body))
+  t.true(res.body.message.includes('verticalExtractCount'), json(res.body))
+})
+
+test('GET /getImageExtracts?sceneName=/../&imageQuality=a&horizontalExtractCount=a&verticalExtractCount=a', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/getImageExtracts?sceneName=/../&imageQuality=a&horizontalExtractCount=a&verticalExtractCount=a`)
+
+  t.is(res.status, 400, json(res))
+  t.true(res.body.message.includes('Invalid query parameter'), json(res.body))
+  t.truthy(res.body.data.find(x => x.match(/The requested scene name.*is not valid/)), json(res.body))
+  t.truthy(res.body.data.find(x => x.includes('The specified quality is not an integer')), json(res.body))
+  t.truthy(res.body.data.find(x => x.includes('horizontal axis is not an integer')), json(res.body))
+  t.truthy(res.body.data.find(x => x.includes('vertical axis is not an integer')), json(res.body))
+})
+
+test('GET /getImageExtracts?sceneName=unknown-scene-name&imageQuality=10&horizontalExtractCount=5&verticalExtractCount=2', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/getImageExtracts?sceneName=unknown-scene-name&imageQuality=10&horizontalExtractCount=5&verticalExtractCount=2`)
+
+  t.is(res.status, 500, json(res))
+  t.truthy(res.body.message.match(/Can't access.*scene dir.*Check it exist.*and you have read permission/), json(res.body))
+})
+
+test('GET /getImageExtracts?sceneName=bathroom&imageQuality=99999&horizontalExtractCount=5&verticalExtractCount=2', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/getImageExtracts?sceneName=bathroom&imageQuality=99999&horizontalExtractCount=5&verticalExtractCount=2`)
+
+  t.is(res.status, 404, json(res))
+  t.truthy(res.body.message.match(/requested quality.*not found for.*scene/), json(res.body))
+})
+
+test('GET /getImageExtracts?sceneName=bathroom&imageQuality=10&horizontalExtractCount=0&verticalExtractCount=9999', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/getImageExtracts?sceneName=bathroom&imageQuality=10&horizontalExtractCount=0&verticalExtractCount=9999`)
+
+  t.is(res.status, 400, json(res))
+  t.true(res.body.message.includes('Invalid query parameter'), json(res.body))
+  t.truthy(res.body.data.find(x => x.includes('Incompatible number of horizontal extracts')), json(res.body))
+  t.truthy(res.body.data.find(x => x.includes('Incompatible number of vertical extracts')), json(res.body))
+})
+
+test.serial('GET /getImageExtracts?sceneName=bathroom&imageQuality=10&horizontalExtractCount=5&verticalExtractCount=2', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/getImageExtracts?sceneName=bathroom&imageQuality=10&horizontalExtractCount=5&verticalExtractCount=2`)
+
+  t.is(res.status, 200, json(res))
+  t.true(Array.isArray(res.body.data), json(res.body))
+  t.is(res.body.data[0], `${imageServedUrl}/bathroom/extracts/x5_y2/zone00001/bathroom_zone00001_10.png`, json(res.body))
+
+  // Check link is accessible and is an image
+  const res2 = await request(t.context.server)
+    .get(`${imageServedUrl}/bathroom/extracts/x5_y2/zone00001/bathroom_zone00001_10.png`)
+
+  t.is(res2.status, 200, json(res2))
+  t.is(res2.header['content-type'], 'image/png', json(res2))
+})
+
+test.serial('Extracts were successfully generated', async t => {
+  // Check the extract on the file system
+  const extracts = path.resolve(imagesPath, 'bathroom', 'extracts')
+  const aBathroomConfig = path.resolve(extracts, 'x5_y2')
+  const aBathroomConfigZone = path.resolve(aBathroomConfig, 'zone00001')
+  const aBathroomConfigZoneImg = path.resolve(aBathroomConfigZone, 'bathroom_zone00001_10.png')
+  const fsp = fs.promises
+  // Check `bathroom/extracts`
+  t.true(await fs.pathExists(extracts))
+  t.deepEqual(await fsp.readdir(extracts), ['x5_y2'])
+
+  // Check `bathroom/extracts/x5_y2`
+  t.true(await fs.pathExists(aBathroomConfig), aBathroomConfig)
+  t.is((await fsp.readdir(aBathroomConfig)).length, 10)
+
+  // Check `bathroom/extracts/x5_y2/zone00001`
+  t.true(await fs.pathExists(aBathroomConfigZone))
+  t.is((await fsp.readdir(aBathroomConfigZone)).length, 1)
+
+  // Check `bathroom/extracts/x5_y2/zone00001/bathroom_zone00001_10.png`
+  t.true(await fs.pathExists(aBathroomConfigZoneImg))
+})

+ 19 - 0
test/api/listScenes.js

@@ -0,0 +1,19 @@
+'use strict'
+
+import test from 'ava'
+import request from 'supertest'
+import { apiPrefix } from '../../config'
+import { json, beforeEachTests } from './_test_functions'
+
+// ROUTE /listScenes
+
+// Before each tests, start a server
+test.beforeEach(beforeEachTests)
+
+test('GET /listScenes', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/listScenes`)
+
+  t.is(res.status, 200, json(res))
+  t.deepEqual(res.body.data, ['bathroom', 'contemporary'], json(res.body))
+})

+ 44 - 0
test/api/listScenesQualities.js

@@ -0,0 +1,44 @@
+'use strict'
+
+import test from 'ava'
+import request from 'supertest'
+import { apiPrefix } from '../../config'
+import { json, beforeEachTests } from './_test_functions'
+
+// ROUTE /listSceneQualities
+
+// Before each tests, start a server
+test.beforeEach(beforeEachTests)
+
+test('GET /listSceneQualities', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/listSceneQualities`)
+
+  t.is(res.status, 400, json(res))
+  t.true(res.body.message.includes('Missing parameter'), json(res.body))
+  t.true(res.body.message.includes('sceneName'), json(res.body))
+})
+
+test('GET /listSceneQualities?sceneName=invalid/../scene', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/listSceneQualities?sceneName=invalid/../scene`)
+
+  t.is(res.status, 409, json(res))
+  t.truthy(res.body.message.match(/The requested scene name.*is not valid/), json(res.body))
+})
+
+test('GET /listSceneQualities?sceneName=unknown-scene-name', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/listSceneQualities?sceneName=unknown-scene-name`)
+
+  t.is(res.status, 500, json(res))
+  t.truthy(res.body.message.match(/Can't access.*scene dir.*Check it exist.*and you have read permission/), json(res.body))
+})
+
+test('GET /listSceneQualities?sceneName=bathroom', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/listSceneQualities?sceneName=bathroom`)
+
+  t.is(res.status, 200, json(res))
+  t.true(Array.isArray(res.body.data) && res.body.data.every(x => Number.isInteger(x)), json(res.body))
+})