|
@@ -11,13 +11,14 @@ fs.stat = jest.fn()
|
|
|
const os = require('os')
|
|
|
os.hostname = jest.fn().mockImplementation(() => 'fakehost')
|
|
|
|
|
|
-const mockSpawn = jest.fn()
|
|
|
const mockSend = jest.fn()
|
|
|
-function PythonWorker (args) {
|
|
|
- this.spawn = mockSpawn
|
|
|
- this.send = mockSend
|
|
|
+const mockSpawn = jest.fn()
|
|
|
+const PythonWorker = function () {
|
|
|
+ return { send: mockSend, spawn: mockSpawn }
|
|
|
}
|
|
|
+
|
|
|
const interfaces = rewire('../../src/interfaces')
|
|
|
+const { resolvers, typeDefs } = interfaces
|
|
|
interfaces.__set__('PythonWorker', PythonWorker)
|
|
|
|
|
|
/**
|
|
@@ -32,8 +33,6 @@ function AsyncMock ({ result, error }) {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-const { resolvers, typeDefs, __test__ } = interfaces
|
|
|
-
|
|
|
describe('interfaces module', () => {
|
|
|
it('exports resolvers and typeDefs', () => {
|
|
|
expect(resolvers).toBeDefined()
|
|
@@ -42,23 +41,73 @@ describe('interfaces module', () => {
|
|
|
expect(resolvers).toHaveProperty('Mutation')
|
|
|
})
|
|
|
|
|
|
- it('finds workers', async () => {
|
|
|
- // Check that all possible return values of fs.readdir are correctly handled.
|
|
|
+ it('has initialized a state', () => {
|
|
|
+ const modState = interfaces.__get__('state')
|
|
|
+ expect(modState).toMatchObject({
|
|
|
+ interfaces: expect.arrayContaining([]),
|
|
|
+ ports: expect.arrayContaining([]),
|
|
|
+ connections: expect.arrayContaining([])
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it('handles state correctly', async () => {
|
|
|
+ const setState = interfaces.__get__('setState')
|
|
|
+ const revert = interfaces.__set__({
|
|
|
+ state: { test1: 'test1' }
|
|
|
+ })
|
|
|
+
|
|
|
+ // 1. Add a new property
|
|
|
+ await expect(setState({ test2: 'test2' })).resolves
|
|
|
+ expect(interfaces.__get__('state')).toEqual({
|
|
|
+ test1: 'test1',
|
|
|
+ test2: 'test2'
|
|
|
+ })
|
|
|
+
|
|
|
+ // 2. Update an existing property
|
|
|
+ await expect(setState({ test2: 'TEST2' })).resolves
|
|
|
+ expect(interfaces.__get__('state')).toEqual({
|
|
|
+ test1: 'test1',
|
|
|
+ test2: 'TEST2'
|
|
|
+ })
|
|
|
+
|
|
|
+ // 3. Let a function change the state
|
|
|
+ const mockStateChange = jest.fn().mockReturnValueOnce({
|
|
|
+ test: 'from function'
|
|
|
+ })
|
|
|
+ await expect(setState(mockStateChange)).resolves
|
|
|
+ expect(mockStateChange).toHaveBeenCalledWith({
|
|
|
+ test1: 'test1',
|
|
|
+ test2: 'TEST2'
|
|
|
+ })
|
|
|
+ expect(interfaces.__get__('state')).toEqual({
|
|
|
+ test: 'from function'
|
|
|
+ })
|
|
|
+ revert()
|
|
|
+ })
|
|
|
+
|
|
|
+ it('finds workers in a directory', async () => {
|
|
|
const findWorkers = interfaces.__get__('findWorkers')
|
|
|
+ fs.readdir.mockReset()
|
|
|
+
|
|
|
+ // Check that all possible return values of fs.readdir are correctly handled.
|
|
|
|
|
|
// 1. Check use case
|
|
|
const files = ['test1_worker.py', 'test2_worker.py']
|
|
|
fs.readdir.mockReturnValueOnce(files)
|
|
|
await expect(findWorkers('python_workers')).resolves.toEqual(files)
|
|
|
+ expect(fs.readdir).toHaveBeenCalledWith('python_workers')
|
|
|
|
|
|
// 2. Check empty directory
|
|
|
fs.readdir.mockReturnValueOnce([])
|
|
|
await expect(findWorkers('/')).resolves.toEqual([])
|
|
|
+ expect(fs.readdir).toHaveBeenCalledWith('/')
|
|
|
|
|
|
// 3. Check argument error
|
|
|
await expect(findWorkers()).rejects.toThrow(
|
|
|
'Directory argument must be a string.'
|
|
|
)
|
|
|
+ fs.readdir.mockReset()
|
|
|
+ expect(fs.readdir).not.toHaveBeenCalled()
|
|
|
|
|
|
// 4. Check non-existing path
|
|
|
fs.readdir.mockImplementation(() => {
|
|
@@ -69,42 +118,56 @@ describe('interfaces module', () => {
|
|
|
await expect(findWorkers('/DOESNTEXIST')).rejects.toThrow(
|
|
|
`ENOENT: no such file or directory, scandir '/DOESNTEXIST'`
|
|
|
)
|
|
|
+ expect(fs.readdir).toHaveBeenCalledWith('/DOESNTEXIST')
|
|
|
+ fs.readdir.mockReset()
|
|
|
})
|
|
|
|
|
|
- it('finds options', async () => {
|
|
|
+ it('gets options from an interface', async () => {
|
|
|
const getOptions = interfaces.__get__('getOptions')
|
|
|
- const options = [{ option1: 'value1' }]
|
|
|
- const workerProcess = new PythonWorker()
|
|
|
+ const options = [{ option1: 'value1', option2: 'value2' }]
|
|
|
+ const mockWorker = new PythonWorker()
|
|
|
|
|
|
// 1. Check the use case
|
|
|
- mockSend.mockReturnValueOnce(AsyncMock({ result: { data: options } }))
|
|
|
- await expect(getOptions(workerProcess)).resolves.toEqual(options)
|
|
|
- expect(mockSend).toHaveBeenCalledWith({ type: 'options' })
|
|
|
+ mockWorker.send.mockReturnValueOnce(
|
|
|
+ AsyncMock({ result: { data: options } })
|
|
|
+ )
|
|
|
+ await expect(getOptions(mockWorker)).resolves.toEqual(options)
|
|
|
+ expect(mockWorker.send).toHaveBeenCalledWith({ type: 'options' })
|
|
|
|
|
|
// 2. Check empty option case
|
|
|
- mockSend.mockReturnValueOnce(AsyncMock({ result: { data: [] } }))
|
|
|
- await expect(getOptions(workerProcess)).resolves.toEqual([])
|
|
|
- expect(mockSend).toHaveBeenCalledWith({ type: 'options' })
|
|
|
+ mockWorker.send.mockReturnValueOnce(AsyncMock({ result: { data: [] } }))
|
|
|
+ await expect(getOptions(mockWorker)).resolves.toEqual([])
|
|
|
+ expect(mockWorker.send).toHaveBeenCalledWith({ type: 'options' })
|
|
|
|
|
|
// 3. Check error case
|
|
|
- mockSend.mockReturnValueOnce(AsyncMock({ result: { error: 'Timeout' } }))
|
|
|
- await expect(getOptions(workerProcess)).rejects.toThrow('Timeout')
|
|
|
+ mockWorker.send.mockReturnValueOnce(
|
|
|
+ AsyncMock({ result: { error: 'Timeout' } })
|
|
|
+ )
|
|
|
+ await expect(getOptions(mockWorker)).rejects.toThrow('Timeout')
|
|
|
|
|
|
- // 4. Throw error if workerProcess doesn't have a "send" property.
|
|
|
- expect(getOptions()).rejects.toThrow(
|
|
|
+ // 4. Throw error if mockWorker doesn't have a "send" property.
|
|
|
+ await expect(getOptions()).rejects.toThrow(
|
|
|
'workerProcess not configured properly.'
|
|
|
)
|
|
|
})
|
|
|
|
|
|
- it('creates an interface', async () => {
|
|
|
+ it('creates an interface from a worker script', async () => {
|
|
|
+ fs.stat.mockReset()
|
|
|
+ mockSend.mockReset()
|
|
|
+ mockSpawn.mockReset()
|
|
|
+ const mockFile = {
|
|
|
+ name: 'test1_worker.py',
|
|
|
+ path: 'python_workers/test1_worker.py',
|
|
|
+ stat: { mtime: now }
|
|
|
+ }
|
|
|
const createInterface = interfaces.__get__('createInterface')
|
|
|
|
|
|
// 1. Check the use case
|
|
|
mockSpawn.mockReturnValueOnce(AsyncMock({ result: { data: 'ready!' } }))
|
|
|
mockSend.mockReturnValueOnce(AsyncMock({ result: { data: [] } }))
|
|
|
- fs.stat.mockReturnValueOnce({ mtime: now, size: 1234 })
|
|
|
+ fs.stat.mockReturnValueOnce(mockFile.stat)
|
|
|
await expect(
|
|
|
- createInterface('python_workers', 'test1_worker.py', [])
|
|
|
+ createInterface('python_workers', mockFile.name, [])
|
|
|
).resolves.toMatchObject({
|
|
|
interfaceName: 'test1',
|
|
|
options: [],
|
|
@@ -113,13 +176,14 @@ describe('interfaces module', () => {
|
|
|
spawn: expect.anything()
|
|
|
}),
|
|
|
workerScript: expect.objectContaining({
|
|
|
- path: 'python_workers/test1_worker.py',
|
|
|
- mtime: now,
|
|
|
+ path: mockFile.path,
|
|
|
+ ...mockFile.stat,
|
|
|
updated: null
|
|
|
})
|
|
|
})
|
|
|
expect(mockSpawn).toHaveBeenCalled()
|
|
|
expect(mockSend).toHaveBeenCalledWith({ type: 'options' })
|
|
|
+ expect(fs.stat).toHaveBeenCalledWith(mockFile.path)
|
|
|
|
|
|
// 2. Check that the update property is set for existing interfaces
|
|
|
|
|
@@ -143,15 +207,14 @@ describe('interfaces module', () => {
|
|
|
)
|
|
|
})
|
|
|
|
|
|
- it('finds interfaces', async () => {
|
|
|
- const findInterfaces = interfaces.__get__('findInterfaces')
|
|
|
+ it('generates the interface list', async () => {
|
|
|
+ const generateInterfaceList = interfaces.__get__('generateInterfaceList')
|
|
|
|
|
|
const mockWorkerList = ['test1_worker.py', 'test2_worker.py']
|
|
|
const mockInterfaceList = [
|
|
|
{ interfaceName: 'test1' },
|
|
|
{ interfaceName: 'test2' }
|
|
|
]
|
|
|
- const moduleWorkerdir = interfaces.__get__('WORKER_DIR')
|
|
|
const mockFindWorkers = jest.fn()
|
|
|
const mockCreateInterface = jest.fn()
|
|
|
const revert = interfaces.__set__({
|
|
@@ -163,13 +226,113 @@ describe('interfaces module', () => {
|
|
|
mockFindWorkers.mockReturnValueOnce(mockWorkerList)
|
|
|
mockCreateInterface.mockReturnValueOnce(mockInterfaceList[0])
|
|
|
mockCreateInterface.mockReturnValueOnce(mockInterfaceList[1])
|
|
|
- await expect(findInterfaces()).resolves.toEqual(mockInterfaceList)
|
|
|
- expect(mockFindWorkers).toHaveBeenCalledWith(moduleWorkerdir)
|
|
|
+ await expect(generateInterfaceList([])).resolves.toEqual(mockInterfaceList)
|
|
|
+ expect(mockFindWorkers).toHaveBeenCalledWith(process.env.WORKER_DIR)
|
|
|
expect(mockCreateInterface).toHaveBeenCalledWith(
|
|
|
- moduleWorkerdir,
|
|
|
+ process.env.WORKER_DIR,
|
|
|
'test1_worker.py',
|
|
|
expect.anything()
|
|
|
)
|
|
|
+ expect(mockCreateInterface).toHaveBeenCalledWith(
|
|
|
+ process.env.WORKER_DIR,
|
|
|
+ 'test2_worker.py',
|
|
|
+ expect.anything()
|
|
|
+ )
|
|
|
revert()
|
|
|
})
|
|
|
+
|
|
|
+ it('resolves interfaces for GraphQL queries', async () => {
|
|
|
+ // 1. calls to generate the interface list.
|
|
|
+ const mockPrevInterfaceList = [
|
|
|
+ { id: 1, interfaceName: 'test1', active: true },
|
|
|
+ { id: 2, interfaceName: 'test2', active: true },
|
|
|
+ { id: 3, interfaceName: 'test3', active: true }
|
|
|
+ ]
|
|
|
+ const mockNextInterfaceList = [
|
|
|
+ { id: 2, interfaceName: 'test2', active: true },
|
|
|
+ { id: 3, interfaceName: 'test3', active: true },
|
|
|
+ { id: 4, interfaceName: 'test4', active: true }
|
|
|
+ ]
|
|
|
+ const mockExpInterfaceList = [
|
|
|
+ { id: 1, interfaceName: 'test1', active: false },
|
|
|
+ { id: 2, interfaceName: 'test2', active: true },
|
|
|
+ { id: 3, interfaceName: 'test3', active: true },
|
|
|
+ { id: 4, interfaceName: 'test4', active: true }
|
|
|
+ ]
|
|
|
+ const mockGenerateInterfaceList = jest.fn()
|
|
|
+ const revert = interfaces.__set__({
|
|
|
+ state: { interfaces: mockPrevInterfaceList },
|
|
|
+ generateInterfaceList: mockGenerateInterfaceList
|
|
|
+ })
|
|
|
+
|
|
|
+ mockGenerateInterfaceList.mockReturnValueOnce(mockNextInterfaceList)
|
|
|
+
|
|
|
+ // 2. returns the updated interface list
|
|
|
+ await expect(resolvers.Query.interfaces()).resolves.toEqual(
|
|
|
+ mockExpInterfaceList
|
|
|
+ )
|
|
|
+ revert()
|
|
|
+ })
|
|
|
+
|
|
|
+ it('resolves a single interface', async () => {
|
|
|
+ await expect(resolvers.Query.interface()).rejects.toThrow(
|
|
|
+ "Cannot destructure property `id` of 'undefined' or 'null'."
|
|
|
+ )
|
|
|
+
|
|
|
+ const mockInterface = { id: 3, interfaceName: 'test3' }
|
|
|
+ const revert = interfaces.__set__({
|
|
|
+ state: { interfaces: [mockInterface] }
|
|
|
+ })
|
|
|
+ await expect(resolvers.Query.interface(null, { id: 3 })).resolves.toEqual(
|
|
|
+ mockInterface
|
|
|
+ )
|
|
|
+ revert()
|
|
|
+ })
|
|
|
+
|
|
|
+ it('creates interface ports', async () => {
|
|
|
+ const createInterfacePorts = interfaces.__get__('createInterfacePorts')
|
|
|
+ const iface = {
|
|
|
+ id: 3,
|
|
|
+ interfaceName: 'test1',
|
|
|
+ workerProcess: new PythonWorker()
|
|
|
+ }
|
|
|
+ const ports = [{ id: 1, device: 'COM1' }, { id: 2, device: 'COM2' }]
|
|
|
+
|
|
|
+ mockSend.mockReturnValueOnce(
|
|
|
+ AsyncMock({
|
|
|
+ result: {
|
|
|
+ data: ports
|
|
|
+ }
|
|
|
+ })
|
|
|
+ )
|
|
|
+ await expect(createInterfacePorts(iface)).resolves.toEqual([
|
|
|
+ expect.objectContaining(ports[0]),
|
|
|
+ expect.objectContaining(ports[1])
|
|
|
+ ])
|
|
|
+ })
|
|
|
+
|
|
|
+ it('generates the port list', async () => {
|
|
|
+ const generatePortList = interfaces.__get__('generatePortList')
|
|
|
+ const revert = interfaces.__set__({
|
|
|
+ generateInterfaceList: jest
|
|
|
+ .fn()
|
|
|
+ .mockReturnValueOnce([
|
|
|
+ { id: 1, interfaceName: 'test1' },
|
|
|
+ { id: 2, interfaceName: 'test2' }
|
|
|
+ ]),
|
|
|
+ createInterfacePorts: jest
|
|
|
+ .fn()
|
|
|
+ .mockReturnValueOnce([1, 2])
|
|
|
+ .mockReturnValueOnce([3, 4])
|
|
|
+ })
|
|
|
+
|
|
|
+ // const mockPortList = [{ id: 1, device: 'COM1' }, { id: 2, device: 'COM2' }]
|
|
|
+
|
|
|
+ await expect(generatePortList([])).resolves.toEqual([1, 2, 3, 4])
|
|
|
+ revert()
|
|
|
+ })
|
|
|
+
|
|
|
+ it('resolves ports', async () => {
|
|
|
+ expect(1).toEqual(2)
|
|
|
+ })
|
|
|
})
|