Skip to content

Commit

Permalink
test: setup
Browse files Browse the repository at this point in the history
  • Loading branch information
Ni55aN committed Dec 26, 2024
1 parent ac44122 commit 2936de7
Show file tree
Hide file tree
Showing 9 changed files with 633 additions and 2 deletions.
10 changes: 9 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,13 @@ import tseslint from 'typescript-eslint';
import configs from 'rete-cli/configs/eslint.mjs';

export default tseslint.config(
...configs
...configs,
{
files: ['**/*.test.ts'],
rules: {
'eslint-disable': 'off',
'init-declarations': 'off',
'max-statements': 'off',
}
}
)
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"scripts": {
"build": "rete build -c rete.config.ts",
"lint": "rete lint",
"test": "rete test",
"doc": "rete doc"
},
"author": "Vitaliy Stoliarov",
Expand Down
53 changes: 53 additions & 0 deletions test/control-flow-engine.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { describe, expect, it, jest } from '@jest/globals'
import { ClassicPreset, NodeEditor } from 'rete'

import { ControlFlowEngine, ControlFlowEngineScheme } from '../src/control-flow-engine'

class Node extends ClassicPreset.Node {
constructor(public label: string) {
super(label)

this.addInput('input', new ClassicPreset.Input(new ClassicPreset.Socket('input')))
this.addOutput('output', new ClassicPreset.Output(new ClassicPreset.Socket('output')))
}

execute(input: string, forward: (output: string) => void): void {
forward('output')
}
}

class Connection<N extends Node> extends ClassicPreset.Connection<N, N> {
constructor(public node1: N, public output: string, public node2: N, public input: string) {
super(node1, output, node2, input)
}
}

describe('ControlFlow', () => {
let editor!: NodeEditor<ControlFlowEngineScheme>
let controlFlow: ControlFlowEngine<ControlFlowEngineScheme>

beforeEach(() => {
editor = new NodeEditor<ControlFlowEngineScheme>()
controlFlow = new ControlFlowEngine()

editor.use(controlFlow)
})

it('executes sequence of nodes', async () => {
const node1 = new Node('label1')
const node2 = new Node('label2')

await editor.addNode(node1)
await editor.addNode(node2)

await editor.addConnection(new Connection(node1, 'output', node2, 'input'))

const spy1 = jest.spyOn(node1, 'execute')
const spy2 = jest.spyOn(node2, 'execute')

controlFlow.execute(node1.id)

expect(spy1).toHaveBeenCalled()
expect(spy2).toHaveBeenCalled()
})
})
141 changes: 141 additions & 0 deletions test/control-flow.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { describe, expect, it } from '@jest/globals'
import { ClassicPreset, NodeEditor } from 'rete'

import { ControlFlow } from '../src/control-flow'
import { ClassicScheme } from '../src/types'

describe('ControlFlow', () => {
let editor!: NodeEditor<ClassicScheme>
let controlFlow: ControlFlow<ClassicScheme>

beforeEach(() => {
editor = new NodeEditor<ClassicScheme>()
controlFlow = new ControlFlow(editor)
})

it('adds a node to the control flow', () => {
const node = new ClassicPreset.Node('label')

controlFlow.add(node, {
inputs: () => [],
outputs: () => [],
execute: () => null
})

expect(controlFlow.setups.has(node.id)).toBe(true)
})

it('removes a node from the control flow', () => {
const node = new ClassicPreset.Node('label')

controlFlow.add(node, {
inputs: () => [],
outputs: () => [],
execute: () => null
})

controlFlow.remove(node.id)

expect(controlFlow.setups.has(node.id)).toBe(false)
})

it('executes a node', () => {
const node = new ClassicPreset.Node('label')
const fn1 = jest.fn()

controlFlow.add(node, {
inputs: () => [],
outputs: () => [],
execute: fn1
})

expect(fn1).not.toHaveBeenCalled()
controlFlow.execute(node.id)
expect(fn1).toHaveBeenCalled()
})

it('executes sequence of nodes', async () => {
const node1 = new ClassicPreset.Node('label')
const node2 = new ClassicPreset.Node('label')

node1.addOutput('out', new ClassicPreset.Output(new ClassicPreset.Socket('number')))
node2.addInput('in', new ClassicPreset.Input(new ClassicPreset.Socket('number')))

await editor.addConnection(new ClassicPreset.Connection(node1, 'out', node2, 'in'))

const fn1 = jest.fn()
const fn2 = jest.fn()

controlFlow.add(node1, {
inputs: () => [],
outputs: () => ['out'],
execute: (input, forward) => {
fn1()
forward('out')
}
})
controlFlow.add(node2, {
inputs: () => ['in'],
outputs: () => [],
execute: fn2
})

expect(fn1).not.toHaveBeenCalled()
expect(fn2).not.toHaveBeenCalled()
controlFlow.execute(node1.id)
expect(fn1).toHaveBeenCalled()
expect(fn2).toHaveBeenCalled()
})

it('throws error when node is not initialized', () => {
const node = new ClassicPreset.Node('label')

expect(() => controlFlow.execute(node.id)).toThrowError('node is not initialized')
})

it('throws error when trying to add a node more than once', () => {
const node = new ClassicPreset.Node('label')

controlFlow.add(node, {
inputs: () => [],
outputs: () => [],
execute: () => null
})

expect(() => controlFlow.add(node, {
inputs: () => [],
outputs: () => [],
execute: () => null
})).toThrowError('already processed')
})

it('throws error when trying to remove a node that does not exist', () => {
const node = new ClassicPreset.Node('label')

expect(() => controlFlow.remove(node.id)).not.toThrow()
})

it('throws error when input is not specified', () => {
const node = new ClassicPreset.Node('label')

controlFlow.add(node, {
inputs: () => [],
outputs: () => [],
execute: () => null
})

expect(() => controlFlow.execute(node.id, 'input')).toThrowError('inputs don\'t have a key')
})

it('throws error when output is not specified', () => {
const node = new ClassicPreset.Node('label')

controlFlow.add(node, {
inputs: () => [],
outputs: () => [],
execute: (inputs, forward) => forward('output')
})

expect(() => controlFlow.execute(node.id)).toThrowError('outputs don\'t have a key')
})
})
170 changes: 170 additions & 0 deletions test/dataflow-engine.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { describe, expect, it, jest } from '@jest/globals'
import { ClassicPreset, NodeEditor } from 'rete'

import { DataflowEngine, DataflowEngineScheme, DataflowNode } from '../src/dataflow-engine'

class Node extends ClassicPreset.Node implements DataflowNode {
constructor(public label: string, private produces: string) {
super(label)

this.addInput('input', new ClassicPreset.Input(new ClassicPreset.Socket('input')))
this.addOutput('output', new ClassicPreset.Output(new ClassicPreset.Socket('output')))
}

data(): { output: string } {
return {
output: this.produces
}
}
}

class Connection<N extends Node> extends ClassicPreset.Connection<N, N> {
constructor(public node1: N, public output: string, public node2: N, public input: string) {
super(node1, output, node2, input)
}
}

describe('ControlFlow', () => {
let editor!: NodeEditor<DataflowEngineScheme>
let dataflow: DataflowEngine<DataflowEngineScheme>

beforeEach(() => {
editor = new NodeEditor<DataflowEngineScheme>()
dataflow = new DataflowEngine()

editor.use(dataflow)
})

it('collects input data from the predecessor nodes', async () => {
const node1 = new Node('label1', 'data1')
const node2 = new Node('label2', 'data2')
const node3 = new Node('label3', 'data3')

await editor.addNode(node1)
await editor.addNode(node2)
await editor.addNode(node3)

await editor.addConnection(new Connection(node1, 'output', node3, 'input'))
await editor.addConnection(new Connection(node2, 'output', node3, 'input'))

const spy1 = jest.spyOn(node1, 'data')
const spy2 = jest.spyOn(node2, 'data')
const spy3 = jest.spyOn(node3, 'data')

expect(spy1).not.toHaveBeenCalled()
expect(spy2).not.toHaveBeenCalled()
expect(spy3).not.toHaveBeenCalled()

await dataflow.fetchInputs(node3.id)

expect(spy1).toHaveBeenCalled()
expect(spy2).toHaveBeenCalled()
expect(spy3).not.toHaveBeenCalled()

expect(spy1).toHaveReturnedWith({ output: 'data1' })
expect(spy2).toHaveReturnedWith({ output: 'data2' })
})

it('fetches data from the leaf node', async () => {
const node1 = new Node('label1', 'data1')
const node2 = new Node('label2', 'data2')
const node3 = new Node('label3', 'data3')

await editor.addNode(node1)
await editor.addNode(node2)
await editor.addNode(node3)

await editor.addConnection(new Connection(node1, 'output', node3, 'input'))
await editor.addConnection(new Connection(node2, 'output', node3, 'input'))

const spy1 = jest.spyOn(node1, 'data')
const spy2 = jest.spyOn(node2, 'data')
const spy3 = jest.spyOn(node3, 'data')

expect(spy1).not.toHaveBeenCalled()
expect(spy2).not.toHaveBeenCalled()
expect(spy3).not.toHaveBeenCalled()

await dataflow.fetch(node3.id)

expect(spy1).toHaveBeenCalled()
expect(spy2).toHaveBeenCalled()
expect(spy3).toHaveBeenCalled()

expect(spy1).toHaveReturnedWith({ output: 'data1' })
expect(spy2).toHaveReturnedWith({ output: 'data2' })
expect(spy3).toHaveReturnedWith({ output: 'data3' })
})

it('caches the data', async () => {
const node = new Node('label', 'data')

await editor.addNode(node)

const spy = jest.spyOn(node, 'data')

expect(spy).not.toHaveBeenCalled()

await dataflow.fetch(node.id)

expect(spy).toHaveBeenCalledTimes(1)

await dataflow.fetch(node.id)

expect(spy).toHaveBeenCalledTimes(1)
})

it('clears the cache', async () => {
const node = new Node('label', 'data')

await editor.addNode(node)

const spy = jest.spyOn(node, 'data')

expect(spy).not.toHaveBeenCalled()

await dataflow.fetch(node.id)

expect(spy).toHaveBeenCalledTimes(1)

dataflow.reset(node.id)
await dataflow.fetch(node.id)

expect(spy).toHaveBeenCalledTimes(2)
})

it('resets the cache of the node and all its successors', async () => {
const node1 = new Node('label1', 'data1')
const node2 = new Node('label2', 'data2')
const node3 = new Node('label3', 'data3')

await editor.addNode(node1)
await editor.addNode(node2)
await editor.addNode(node3)

await editor.addConnection(new Connection(node1, 'output', node2, 'input'))
await editor.addConnection(new Connection(node2, 'output', node3, 'input'))

const spy1 = jest.spyOn(node1, 'data')
const spy3 = jest.spyOn(node3, 'data')

expect(spy1).not.toHaveBeenCalled()
expect(spy3).not.toHaveBeenCalled()

await dataflow.fetch(node3.id)

expect(spy1).toHaveBeenCalled()
expect(spy3).toHaveBeenCalledTimes(1)

await dataflow.fetch(node3.id)

expect(spy1).toHaveBeenCalledTimes(1)
expect(spy3).toHaveBeenCalledTimes(1)

dataflow.reset(node2.id)
await dataflow.fetch(node3.id)

expect(spy1).toHaveBeenCalledTimes(1)
expect(spy3).toHaveBeenCalledTimes(2)
})
})
Loading

0 comments on commit 2936de7

Please sign in to comment.