314 lines
9.7 KiB
Ruby
314 lines
9.7 KiB
Ruby
require 'spec_helper'
|
|
HAS_POINTS = JSON.parse(File.read("spec/lib/celery_script/ast_has_points.json"))
|
|
|
|
describe Api::SequencesController do
|
|
before :each do
|
|
request.headers["accept"] = 'application/json'
|
|
end
|
|
|
|
include Devise::Test::ControllerHelpers
|
|
|
|
describe '#create' do
|
|
let(:user) { FactoryBot.create(:user) }
|
|
let(:nodes) { sequence_body_for(user) }
|
|
|
|
it 'handles a well formed AST in the body attribute' do
|
|
sign_in user
|
|
input = { name: "Scare Birds",
|
|
body: nodes }
|
|
sequence_body_for(user)
|
|
post :create, body: input.to_json, params: {format: :json}
|
|
expect(response.status).to eq(200)
|
|
expect(json[:args]).to be_kind_of(Hash)
|
|
expect(json[:body]).to be_kind_of(Array)
|
|
expect(json[:body].length).to eq(nodes.length)
|
|
end
|
|
|
|
it 'disregards extra attrs (like `uuid`) on sequence body nodes' do
|
|
sign_in user
|
|
input = { name: "Scare Birds",
|
|
body: nodes }
|
|
input[:body].first[:uuid] = SecureRandom.uuid
|
|
input[:body].first["uuid"] = SecureRandom.uuid
|
|
sequence_body_for(user)
|
|
post :create, body: input.to_json, params: {format: :json}
|
|
expect(response.status).to eq(200)
|
|
expect(json[:args]).to be_kind_of(Hash)
|
|
expect(json[:body]).to be_kind_of(Array)
|
|
expect(json[:body].length).to eq(nodes.length)
|
|
end
|
|
|
|
it 'creates a new sequences for a user' do
|
|
sign_in user
|
|
input = { name: "Scare Birds", body: [] }
|
|
post :create, body: input.to_json, format: :json
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
it 'handles invalid params' do
|
|
# Needed to test the `else` branch of mutate() somewhere
|
|
sign_in user
|
|
input = {}
|
|
post :create, body: input.to_json, format: :json
|
|
expect(response.status).to eq(422)
|
|
|
|
expect(json[:name]).to eq("Name is required")
|
|
end
|
|
|
|
it 'doesnt allow nonsense in `sequence.args.locals`' do
|
|
PinBinding.destroy_all
|
|
Sequence.destroy_all
|
|
input = { name: "Scare Birds",
|
|
body: [],
|
|
# Intentional nonsense to check validation logic.
|
|
args: { locals: { kind: "wait", args: { milliseconds: 5000 } } }
|
|
}
|
|
|
|
sign_in user
|
|
post :create, body: input.to_json, params: {format: :json}
|
|
expect(response.status).to eq(422)
|
|
expect(Sequence.last).to_not be
|
|
end
|
|
|
|
it 'strips excess `args`' do
|
|
input = { name: "Scare Birds",
|
|
body: [],
|
|
# Intentional nonsense to check validation logic.
|
|
args: { foo: "BAR" } }
|
|
|
|
sign_in user
|
|
post :create, body: input.to_json, params: {format: :json}
|
|
expect(response.status).to eq(200)
|
|
expect(json[:args][:foo]).to eq(nil)
|
|
generated_result = CeleryScript::FetchCelery
|
|
.run!(sequence: Sequence.find(json[:id]))
|
|
.deep_symbolize_keys
|
|
expect(generated_result.dig(:args, :foo)).to eq(nil)
|
|
end
|
|
|
|
it 'disallows typos in `locals` declaration' do
|
|
input = {
|
|
name: "Scare Birds",
|
|
body: [],
|
|
# Intentional nonsense to check validation logic.
|
|
args: {
|
|
locals: {
|
|
kind: "scope_declaration",
|
|
args: {},
|
|
body: [
|
|
{
|
|
kind: "parameter_declaration",
|
|
args: {
|
|
label: "parent"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
|
|
sign_in user
|
|
post :create, body: input.to_json, params: {format: :json}
|
|
expect(response.status).to eq(422)
|
|
expect(json[:body]).to include("not a valid default value")
|
|
end
|
|
|
|
it 'disallows erroneous `locals` declaration' do
|
|
input = {
|
|
name: "Scare Birds",
|
|
body: [],
|
|
# Intentional nonsense to check validation logic.
|
|
args: {
|
|
locals: {
|
|
kind: "scope_declaration",
|
|
args: {},
|
|
body: [
|
|
{ kind: "wait", args: { milliseconds: 5000 } }
|
|
]
|
|
}
|
|
}
|
|
}
|
|
|
|
sign_in user
|
|
post :create, body: input.to_json, params: {format: :json}
|
|
expect(response.status).to eq(422)
|
|
expctd =
|
|
"Expected one of: [:parameter_declaration, :parameter_declaration]"
|
|
expect(json[:body]).to include(expctd)
|
|
end
|
|
|
|
it 'allows declaration of a variable named `parent`' do
|
|
input = {
|
|
name: "Scare Birds",
|
|
args: {
|
|
locals: {
|
|
kind: "scope_declaration",
|
|
args: {},
|
|
body: [
|
|
{
|
|
kind: "parameter_declaration",
|
|
args: {
|
|
label: "parent",
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
body: [
|
|
{
|
|
kind: "move_absolute",
|
|
args: {
|
|
location: { kind: "identifier", args: { label: "parent" } },
|
|
offset: { kind: "coordinate", args: { x: 0, y: 0, z: 0 } },
|
|
speed: 100,
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
sign_in user
|
|
post :create, body: input.to_json, params: {format: :json}
|
|
expect(response.status).to eq(200)
|
|
dig_path = [:args, :locals, :body, 0, :args, :label]
|
|
generated_result = CeleryScript::FetchCelery
|
|
.run!(sequence: Sequence.find(json[:id]))
|
|
.deep_symbolize_keys
|
|
expect(generated_result.dig(*dig_path)).to eq("parent")
|
|
expect(json.dig(*dig_path)).to eq("parent")
|
|
end
|
|
|
|
it 'tracks Points' do
|
|
point = FactoryBot.create(:generic_pointer, device: user.device)
|
|
PinBinding.destroy_all
|
|
Sequence.destroy_all
|
|
EdgeNode.destroy_all
|
|
PrimaryNode.destroy_all
|
|
HAS_POINTS["body"][0]["args"]["location"]["args"]["pointer_id"] =
|
|
point.id
|
|
sign_in user
|
|
input = { name: "Scare Birds", body: HAS_POINTS["body"] }
|
|
sequence_body_for(user)
|
|
before = EdgeNode.where(kind: "pointer_id").count
|
|
post :create, body: input.to_json, params: {format: :json}
|
|
expect(response.status).to eq(200)
|
|
now = EdgeNode.where(kind: "pointer_id").count
|
|
expect(now).to be > before
|
|
end
|
|
|
|
it 'prevents unbound variables' do
|
|
sign_in user
|
|
input = {
|
|
name: "Unbound Variable Exception",
|
|
args: { locals: Sequence::SCOPE_DECLARATION },
|
|
body: [
|
|
{
|
|
kind: "move_absolute",
|
|
args: {
|
|
location: {
|
|
kind: "identifier",
|
|
args: { label: "parent" }
|
|
},
|
|
offset: {
|
|
kind: "coordinate",
|
|
args: { x: 0, y: 0, z: 0 }
|
|
},
|
|
speed: 100,
|
|
}
|
|
}
|
|
]
|
|
}
|
|
post :create, body: input.to_json, params: {format: :json}
|
|
expect(response.status).to eq(422)
|
|
expect(json[:body]).to eq("Unbound variable: parent")
|
|
end
|
|
|
|
it 'does not let you use other peoples point resources' do
|
|
sign_in user
|
|
not_yours = FactoryBot.create(:plant)
|
|
expect(not_yours.device_id).to_not eq(user.device_id)
|
|
input = {
|
|
name: "bad point usage",
|
|
args: { locals: { kind: "scope_declaration", args: {} } },
|
|
body: [
|
|
{
|
|
kind: "move_absolute",
|
|
args: {
|
|
location: {
|
|
kind: "point",
|
|
args: { pointer_type: "Plant", pointer_id: not_yours.id }
|
|
},
|
|
speed: 100,
|
|
offset: { kind: "coordinate", args: { x: 0, y: 0, z: 0 } }
|
|
}
|
|
}
|
|
],
|
|
}
|
|
post :create, body: input.to_json, params: {format: :json}
|
|
expect(response.status).to eq(422)
|
|
expect(json[:body]).to include("Bad point ID")
|
|
end
|
|
|
|
it 'prevents type errors from bad identifier / binding combos' do
|
|
sign_in user
|
|
input = { name: "type mismatch",
|
|
args: {
|
|
locals: {
|
|
kind: "scope_declaration",
|
|
args: {},
|
|
body: [
|
|
{
|
|
kind: "parameter_declaration",
|
|
args: { label: "parent" }
|
|
}
|
|
]
|
|
}
|
|
},
|
|
body: [
|
|
{ kind: "move_absolute",
|
|
args: {
|
|
location: { kind: "identifier", args: { label: "parent" } },
|
|
offset: {
|
|
kind: "coordinate",
|
|
args: { x: 0, y: 0, z: 0 }
|
|
},
|
|
speed: 100,
|
|
}
|
|
}
|
|
]
|
|
}
|
|
post :create, body: input.to_json, params: {format: :json}
|
|
expect(response.status).to eq(422)
|
|
expect(json[:body]).to include("not a valid default value")
|
|
end
|
|
|
|
|
|
it 'provides human readable errors for "nothing" mismatches' do
|
|
sign_in user
|
|
input = { name: "type mismatch",
|
|
args: {
|
|
locals: {
|
|
kind: "scope_declaration",
|
|
args: { },
|
|
body: [
|
|
{
|
|
kind: "parameter_declaration",
|
|
args: {
|
|
label: "x",
|
|
data_value: {
|
|
kind: "nothing",
|
|
args: {}
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
body: [ ]
|
|
}
|
|
post :create, body: input.to_json, params: {format: :json}
|
|
expect(response.status).to eq(422)
|
|
expect(json[:body]).to include("must provide a value for all parameters")
|
|
end
|
|
end
|
|
end
|