diff --git a/lib/floe/workflow/reference_path.rb b/lib/floe/workflow/reference_path.rb index 5650145a..2633c0fa 100644 --- a/lib/floe/workflow/reference_path.rb +++ b/lib/floe/workflow/reference_path.rb @@ -3,16 +3,6 @@ module Floe class Workflow class ReferencePath < Path - class << self - def get(payload, context) - new(payload).get(context) - end - - def set(payload, context, value) - new(payload).set(context, value) - end - end - attr_reader :path def initialize(*) diff --git a/lib/floe/workflow/states/task.rb b/lib/floe/workflow/states/task.rb index 47c3e522..c0a7e626 100644 --- a/lib/floe/workflow/states/task.rb +++ b/lib/floe/workflow/states/task.rb @@ -142,10 +142,6 @@ def parse_output(output) rescue JSON::ParserError nil end - - def next_state - end? ? nil : @next - end end end end diff --git a/spec/floe_spec.rb b/spec/floe_spec.rb new file mode 100644 index 00000000..5af6ea7c --- /dev/null +++ b/spec/floe_spec.rb @@ -0,0 +1,13 @@ +RSpec.describe Floe do + describe "logger=", "logger" do + it "sets the logger" do + old_logger = Floe.logger + new_logger = "abc" + + Floe.logger = new_logger + expect(Floe.logger).to eq(new_logger) + + Floe.logger = old_logger + end + end +end diff --git a/spec/runner_spec.rb b/spec/runner_spec.rb new file mode 100644 index 00000000..8b80e306 --- /dev/null +++ b/spec/runner_spec.rb @@ -0,0 +1,78 @@ +RSpec.describe Floe::Runner do + let(:runner) { described_class.new } + + describe ".register_scheme", ".for_resource" do + it "registers a scheme" do + x = Class.new + y = Class.new + + described_class.register_scheme("x", x) + described_class.register_scheme("y", y) + + expect(described_class.for_resource("x://abc")).to eq(x) + expect(described_class.for_resource("y://abc")).to eq(y) + end + + it "overrides a scheme" do + x = Class.new + x2 = Class.new + + described_class.register_scheme("x", x) + described_class.register_scheme("x", x2) + + expect(described_class.for_resource("x://abc")).to eq(x2) + end + + it "resolves a scheme lambda" do + x = Class.new + + described_class.register_scheme("x", -> { x }) + + expect(described_class.for_resource("x://abc")).to eq(x) + end + end + + # interface methods (not implemented) + + describe "#run_async!" do + it "is not implemented" do + expect { runner.run_async!("local://resource") }.to raise_error(NotImplementedError) + end + end + + describe "#status!" do + it "is not implemented" do + expect { runner.status!({}) }.to raise_error(NotImplementedError) + end + end + + describe "#running?" do + it "is not implemented" do + expect { runner.running?({}) }.to raise_error(NotImplementedError) + end + end + + describe "#success?" do + it "is not implemented" do + expect { runner.success?({}) }.to raise_error(NotImplementedError) + end + end + + describe "#output" do + it "is not implemented" do + expect { runner.output({}) }.to raise_error(NotImplementedError) + end + end + + describe "#cleanup" do + it "is not implemented" do + expect { runner.cleanup({}) }.to raise_error(NotImplementedError) + end + end + + describe "#wait" do + it "is not implemented" do + expect { runner.wait }.to raise_error(NotImplementedError) + end + end +end diff --git a/spec/workflow/choice_rule_spec.rb b/spec/workflow/choice_rule_spec.rb index b08209ad..92e287f5 100644 --- a/spec/workflow/choice_rule_spec.rb +++ b/spec/workflow/choice_rule_spec.rb @@ -3,6 +3,14 @@ let(:subject) { described_class.build(payload).true?(context, input) } let(:context) { {} } + context "Abstract Interface" do + let(:input) { {} } + let(:subject) { described_class.new({}).true?(context, input) } + it "is not implemented" do + expect { subject }.to raise_exception(NotImplementedError) + end + end + context "Boolean Expression" do context "Not" do let(:payload) { {"Not" => {"Variable" => "$.foo", "StringEquals" => "bar"}, "Next" => "FirstMatchState"} } diff --git a/spec/workflow/context_spec.rb b/spec/workflow/context_spec.rb index 74753929..1111d6c1 100644 --- a/spec/workflow/context_spec.rb +++ b/spec/workflow/context_spec.rb @@ -78,6 +78,8 @@ it "ended" do ctx.state["Output"] = input.dup + ctx.execution["StartTime"] ||= Time.now.utc + ctx.execution["EndTime"] ||= Time.now.utc expect(ctx.failed?).to eq(false) end @@ -91,6 +93,34 @@ end end + describe "#success?" do + it "new context" do + expect(ctx.success?).to eq(false) + end + + it "started" do + ctx.state["Output"] = {} + + expect(ctx.success?).to eq(false) + end + + it "ended" do + ctx.state["Output"] = input.dup + ctx.execution["StartTime"] ||= Time.now.utc + ctx.execution["EndTime"] ||= Time.now.utc + + expect(ctx.success?).to eq(true) + end + + it "ended with error" do + ctx.execution["StartTime"] ||= Time.now.utc + ctx.execution["EndTime"] ||= Time.now.utc + ctx.output = {"Cause" => "issue", "Error" => "error"} + + expect(ctx.success?).to eq(false) + end + end + describe "#input" do it "started" do ctx.state["Input"] = input diff --git a/spec/workflow/payload_template_spec.rb b/spec/workflow/payload_template_spec.rb index 49197829..7b540509 100644 --- a/spec/workflow/payload_template_spec.rb +++ b/spec/workflow/payload_template_spec.rb @@ -2,12 +2,21 @@ let(:subject) { described_class.new(payload) } describe "#value" do + context "with static array" do + let(:payload) { ["a", 2] } + let(:context) { {} } + + it "returns the original value" do + expect(subject.value(context)).to eq(["a", 2]) + end + end + context "with static values" do - let(:payload) { {"foo" => "bar", "bar" => "$.baz"} } + let(:payload) { {"foo" => "bar", "bar" => "$.baz", "baz" => 3} } let(:context) { {} } it "returns the original value" do - expect(subject.value(context)).to eq({"foo" => "bar", "bar" => "$.baz"}) + expect(subject.value(context)).to eq({"foo" => "bar", "bar" => "$.baz", "baz" => 3}) end end diff --git a/spec/workflow/state_spec.rb b/spec/workflow/state_spec.rb new file mode 100644 index 00000000..d340d896 --- /dev/null +++ b/spec/workflow/state_spec.rb @@ -0,0 +1,45 @@ +RSpec.describe Floe::Workflow::State do + let(:input) { {} } + let(:ctx) { Floe::Workflow::Context.new(:input => input) } + let(:state) { workflow.current_state } + # picked a state that doesn't instantly finish + let(:workflow) { make_workflow(ctx, {"WaitState" => {"Type" => "Wait", "Seconds" => 1, "Next" => "SuccessState"}, "SuccessState" => {"Type" => "Succeed"}}) } + + describe "#started?" do + it "is not started yet" do + expect(state.started?).to eq(false) + end + + it "is started" do + state.start(ctx.input) + expect(state.started?).to eq(true) + end + + it "is finished" do + state.start(ctx.input) + state.finish + + state.start(ctx.input) + expect(state.started?).to eq(true) + end + end + + describe "#finished?" do + it "is not started yet" do + expect(state.finished?).to eq(false) + end + + it "is started" do + state.start(ctx.input) + expect(state.finished?).to eq(false) + end + + it "is finished" do + state.start(ctx.input) + state.finish + + state.start(ctx.input) + expect(state.finished?).to eq(true) + end + end +end diff --git a/spec/workflow_spec.rb b/spec/workflow_spec.rb index d69bc6cb..8c10bf06 100644 --- a/spec/workflow_spec.rb +++ b/spec/workflow_spec.rb @@ -34,6 +34,10 @@ expect { described_class.new(payload) }.to raise_error(Floe::InvalidWorkflowError, "Missing field \"States\"") end + it "raises an exception for invalid States" do + expect { make_workflow(ctx, {"FirstState" => {"Type" => "Invalid"}}) }.to raise_error(Floe::InvalidWorkflowError, "Invalid state type: [Invalid]") + end + it "raises an exception for missing StartAt" do payload = {"Comment" => "Test", "States" => {}} @@ -58,6 +62,10 @@ expect { described_class.new(payload) }.to raise_error(Floe::InvalidWorkflowError, /must be less than or equal to 80 characters/) end + + it "raises an exception for invalid context" do + expect { described_class.new(make_payload({"Start" => {"Type" => "Success"}}), "abc") }.to raise_error(Floe::InvalidWorkflowError, /unexpected token/) + end end describe "#run_nonblock" do