Skip to content

Commit cd0cb7c

Browse files
committed
Add basic specs
1 parent c491dd8 commit cd0cb7c

3 files changed

Lines changed: 234 additions & 3 deletions

File tree

gem/spec/spec_helper.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
# frozen_string_literal: true
22

33
require 'triangulum'
4+
require 'tucana'
5+
6+
Tucana.load_protocol(:shared)
7+
8+
Dir[File.join(__dir__, 'support/**/*.rb')].each { |f| require f }
49

510
RSpec.configure do |config|
6-
# Enable flags like --only-failures and --next-failure
711
config.example_status_persistence_file_path = '.rspec_status'
8-
9-
# Disable RSpec exposing methods globally on `Module` and `main`
1012
config.disable_monkey_patching!
13+
config.include ProtobufFactories
1114

1215
config.expect_with :rspec do |c|
1316
c.syntax = :expect
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# frozen_string_literal: true
2+
3+
module ProtobufFactories
4+
def build_flow(node_functions:, starting_node_id: 1)
5+
Tucana::Shared::ValidationFlow.new(
6+
flow_id: 1,
7+
project_id: 1,
8+
type: 'test',
9+
starting_node_id: starting_node_id,
10+
node_functions: node_functions,
11+
project_slug: 'test'
12+
)
13+
end
14+
15+
def literal_value(value)
16+
Tucana::Shared::NodeValue.new(
17+
literal_value: Tucana::Shared::Value.from_ruby(value)
18+
)
19+
end
20+
21+
def reference_node(node_id:, paths: [])
22+
Tucana::Shared::NodeValue.new(
23+
reference_value: Tucana::Shared::ReferenceValue.new(
24+
node_id: node_id,
25+
paths: paths
26+
)
27+
)
28+
end
29+
30+
def node_function_value(node_id)
31+
Tucana::Shared::NodeValue.new(node_function_id: node_id)
32+
end
33+
34+
def node(id:, function_id:, parameters: [], next_node_id: nil)
35+
Tucana::Shared::NodeFunction.new(
36+
database_id: id,
37+
runtime_function_id: function_id,
38+
parameters: parameters,
39+
next_node_id: next_node_id,
40+
definition_source: 'test'
41+
)
42+
end
43+
44+
def param(id:, runtime_parameter_id:, value: nil)
45+
Tucana::Shared::NodeParameter.new(
46+
database_id: id,
47+
runtime_parameter_id: runtime_parameter_id,
48+
value: value
49+
)
50+
end
51+
52+
def default_data_types
53+
[
54+
Tucana::Shared::DefinitionDataType.new(identifier: 'LIST', type: 'T[]', generic_keys: ['T']),
55+
Tucana::Shared::DefinitionDataType.new(identifier: 'NUMBER', type: 'number'),
56+
Tucana::Shared::DefinitionDataType.new(identifier: 'STRING', type: 'string'),
57+
Tucana::Shared::DefinitionDataType.new(identifier: 'CONSUMER', type: '(item:R) => void', generic_keys: ['R']),
58+
Tucana::Shared::DefinitionDataType.new(identifier: 'RUNNABLE', type: '() => void')
59+
]
60+
end
61+
62+
def default_functions
63+
[
64+
Tucana::Shared::RuntimeFunctionDefinition.new(
65+
runtime_name: 'std::math::add',
66+
signature: '(a: NUMBER, b: NUMBER): NUMBER'
67+
),
68+
Tucana::Shared::RuntimeFunctionDefinition.new(
69+
runtime_name: 'std::list::at',
70+
signature: '<R>(list: LIST<R>, index: NUMBER): R'
71+
),
72+
Tucana::Shared::RuntimeFunctionDefinition.new(
73+
runtime_name: 'std::control::for_each',
74+
signature: '<R>(list: LIST<R>, consumer: CONSUMER<R>): void'
75+
),
76+
Tucana::Shared::RuntimeFunctionDefinition.new(
77+
runtime_name: 'std::control::return',
78+
signature: '<R>(value: R): R'
79+
)
80+
]
81+
end
82+
end
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe Triangulum::Validation do
4+
let(:data_types) { default_data_types }
5+
let(:functions) { default_functions }
6+
7+
describe '#validate' do
8+
it 'validates a simple valid flow' do
9+
flow = build_flow(
10+
node_functions: [
11+
node(
12+
id: 1,
13+
function_id: 'std::math::add',
14+
parameters: [
15+
param(id: 1, runtime_parameter_id: 'a', value: literal_value(1)),
16+
param(id: 2, runtime_parameter_id: 'b', value: literal_value(0))
17+
]
18+
)
19+
]
20+
)
21+
22+
result = described_class.new(flow, functions, data_types).validate
23+
24+
expect(result.valid?).to be true
25+
expect(result.diagnostics).to be_empty
26+
end
27+
28+
it 'detects type errors in parameters' do
29+
flow = build_flow(
30+
node_functions: [
31+
node(
32+
id: 1,
33+
function_id: 'std::math::add',
34+
parameters: [
35+
param(id: 1, runtime_parameter_id: 'a', value: literal_value('not a number')),
36+
param(id: 2, runtime_parameter_id: 'b', value: literal_value(10))
37+
]
38+
)
39+
]
40+
)
41+
42+
result = described_class.new(flow, functions, data_types).validate
43+
44+
expect(result.valid?).to be false
45+
expect(result.diagnostics).not_to be_empty
46+
expect(result.diagnostics.first.message).to include('number')
47+
end
48+
49+
it 'validates a flow with references between nodes' do
50+
flow = build_flow(
51+
node_functions: [
52+
node(
53+
id: 1,
54+
function_id: 'std::math::add',
55+
next_node_id: 2,
56+
parameters: [
57+
param(id: 1, runtime_parameter_id: 'a', value: literal_value(1)),
58+
param(id: 2, runtime_parameter_id: 'b', value: literal_value(2))
59+
]
60+
),
61+
node(
62+
id: 2,
63+
function_id: 'std::math::add',
64+
parameters: [
65+
param(id: 3, runtime_parameter_id: 'a', value: reference_node(node_id: 1)),
66+
param(id: 4, runtime_parameter_id: 'b', value: literal_value(3))
67+
]
68+
)
69+
]
70+
)
71+
72+
result = described_class.new(flow, functions, data_types).validate
73+
74+
expect(result.valid?).to be true
75+
expect(result.diagnostics).to be_empty
76+
end
77+
78+
it 'validates a flow with nested scopes' do
79+
flow = build_flow(
80+
node_functions: [
81+
node(
82+
id: 1,
83+
function_id: 'std::control::for_each',
84+
parameters: [
85+
param(id: 1, runtime_parameter_id: 'list', value: literal_value([1, 2, 3])),
86+
param(id: 2, runtime_parameter_id: 'consumer', value: node_function_value(2))
87+
]
88+
),
89+
node(
90+
id: 2,
91+
function_id: 'std::math::add',
92+
parameters: [
93+
param(id: 3, runtime_parameter_id: 'a', value: literal_value(1)),
94+
param(id: 4, runtime_parameter_id: 'b', value: literal_value(2))
95+
]
96+
)
97+
]
98+
)
99+
100+
result = described_class.new(flow, functions, data_types).validate
101+
102+
expect(result.valid?).to be true
103+
end
104+
105+
it 'returns diagnostics with node_id and parameter_index' do
106+
flow = build_flow(
107+
node_functions: [
108+
node(
109+
id: 1,
110+
function_id: 'std::math::add',
111+
parameters: [
112+
param(id: 1, runtime_parameter_id: 'a', value: literal_value('string')),
113+
param(id: 2, runtime_parameter_id: 'b', value: literal_value(10))
114+
]
115+
)
116+
]
117+
)
118+
119+
result = described_class.new(flow, functions, data_types).validate
120+
121+
expect(result.valid?).to be false
122+
diagnostic = result.diagnostics.find { |d| d.parameter_index == 0 }
123+
expect(diagnostic).not_to be_nil
124+
expect(diagnostic.node_id).not_to be_nil
125+
end
126+
127+
it 'returns the return type' do
128+
flow = build_flow(
129+
node_functions: [
130+
node(
131+
id: 1,
132+
function_id: 'std::math::add',
133+
parameters: [
134+
param(id: 1, runtime_parameter_id: 'a', value: literal_value(1)),
135+
param(id: 2, runtime_parameter_id: 'b', value: literal_value(2))
136+
]
137+
)
138+
]
139+
)
140+
141+
result = described_class.new(flow, functions, data_types).validate
142+
143+
expect(result.return_type).to eq('void')
144+
end
145+
end
146+
end

0 commit comments

Comments
 (0)