nestene 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +15 -0
- data/.rspec +1 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +2 -0
- data/TODO.txt +7 -0
- data/api.rb +8 -0
- data/autons.rb +13 -0
- data/bin/nestene.rb +5 -0
- data/config.ru +21 -0
- data/features/api/api_create_auton.feature +6 -0
- data/features/api/api_credentials.feature +11 -0
- data/features/api/api_list_of_autons.feature +6 -0
- data/features/callback.feature +13 -0
- data/features/create_auton.feature +11 -0
- data/features/credentials.feature +7 -0
- data/features/execute_step.feature +59 -0
- data/features/restart.feature +7 -0
- data/features/schedule_steps.feature +7 -0
- data/features/step_definitions/api/api_create_auton_steps.rb +16 -0
- data/features/step_definitions/api/api_credentials_steps.rb +22 -0
- data/features/step_definitions/api/api_list_of_autons_steps.rb +8 -0
- data/features/step_definitions/callback_steps.rb +75 -0
- data/features/step_definitions/create_auton_steps.rb +32 -0
- data/features/step_definitions/credentials_steps.rb +24 -0
- data/features/step_definitions/execute_step_steps.rb +177 -0
- data/features/step_definitions/restart_steps.rb +5 -0
- data/features/step_definitions/schedule_steps_steps.rb +29 -0
- data/features/step_definitions/time_delayed_steps_steps.rb +103 -0
- data/features/step_definitions/ui/create_auton_steps.rb +21 -0
- data/features/step_definitions/ui/list_of_autons_steps.rb +18 -0
- data/features/step_definitions/ui/schedule_auton_step_steps.rb +64 -0
- data/features/step_definitions/ui/show_auton_steps.rb +49 -0
- data/features/support/env.rb +36 -0
- data/features/time_delayed_steps.feature +28 -0
- data/features/ui/create_auton.feature +7 -0
- data/features/ui/list_of_autons.feature +7 -0
- data/features/ui/schedule_auton_step.feature +12 -0
- data/features/ui/show_auton.feature +14 -0
- data/lib/nestene/actor/auton_queue.rb +87 -0
- data/lib/nestene/actor/auton_storage.rb +50 -0
- data/lib/nestene/actor/core.rb +150 -0
- data/lib/nestene/actor/delayed_scheduler.rb +76 -0
- data/lib/nestene/auton_context.rb +31 -0
- data/lib/nestene/auton_execution_queue.rb +43 -0
- data/lib/nestene/auton_state.rb +27 -0
- data/lib/nestene/callback.rb +7 -0
- data/lib/nestene/delayed_method.rb +17 -0
- data/lib/nestene/executed_method.rb +29 -0
- data/lib/nestene/executing_method.rb +23 -0
- data/lib/nestene/execution_error.rb +39 -0
- data/lib/nestene/scheduled_method.rb +21 -0
- data/lib/nestene/storage.rb +72 -0
- data/lib/nestene/ui/app.rb +103 -0
- data/lib/nestene/ui/public/app/application.js +75 -0
- data/lib/nestene/ui/public/vendor/angular-ui-sortable/sortable.min.js +1 -0
- data/lib/nestene/ui/public/vendor/angular.min.js +248 -0
- data/lib/nestene/ui/public/vendor/bootstrap/css/bootstrap-theme.css +347 -0
- data/lib/nestene/ui/public/vendor/bootstrap/css/bootstrap-theme.min.css +7 -0
- data/lib/nestene/ui/public/vendor/bootstrap/css/bootstrap.css +5785 -0
- data/lib/nestene/ui/public/vendor/bootstrap/css/bootstrap.min.css +7 -0
- data/lib/nestene/ui/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
- data/lib/nestene/ui/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.svg +229 -0
- data/lib/nestene/ui/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/lib/nestene/ui/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
- data/lib/nestene/ui/public/vendor/bootstrap/js/bootstrap.js +1951 -0
- data/lib/nestene/ui/public/vendor/bootstrap/js/bootstrap.min.js +6 -0
- data/lib/nestene/ui/public/vendor/jquery-ui.min.js +13 -0
- data/lib/nestene/ui/public/vendor/jquery.bootstrap-growl.min.js +1 -0
- data/lib/nestene/ui/public/vendor/jquery.min.js +5 -0
- data/lib/nestene/ui/public/vendor/json-editor/directives.js +245 -0
- data/lib/nestene/ui/public/vendor/json-editor/styles.css +165 -0
- data/lib/nestene/ui/views/auton.haml +42 -0
- data/lib/nestene/ui/views/auton_form.haml +8 -0
- data/lib/nestene/ui/views/index.haml +35 -0
- data/lib/nestene/ui/views/layout.haml +40 -0
- data/lib/nestene/ui/views/schedule_step.haml +14 -0
- data/lib/nestene/version.rb +3 -0
- data/lib/nestene.rb +50 -0
- data/nestene.gemspec +39 -0
- data/spec/nestene/auton_execution_queue_spec.rb +78 -0
- data/spec/nestene/nestene_spec.rb +137 -0
- data/spec/nestene/storage_spec.rb +125 -0
- data/spec/spec_helper.rb +2 -0
- metadata +373 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
.jumbotron
|
2
|
+
%p= @auton_id
|
3
|
+
%p
|
4
|
+
%span#auton_state.label.label-default= @auton_state.state
|
5
|
+
.panel.panel-default
|
6
|
+
.panel-heading Actions
|
7
|
+
.panel-body
|
8
|
+
%a.btn.btn-default#schedule_step{href: url('/auton/%s/schedule_step') % @auton_id} Schedule Step
|
9
|
+
|
10
|
+
.panel.panel-default
|
11
|
+
.panel-heading State
|
12
|
+
.panel-body
|
13
|
+
%code#current_state{'data-toggle' => "tooltip", 'data-placement' => "left", title:"Tooltip on left"}
|
14
|
+
= JSON.pretty_generate(@auton_state.serialized)
|
15
|
+
|
16
|
+
.panel.panel-default
|
17
|
+
#queue.panel-heading Queue
|
18
|
+
.panel-body.container-fluid
|
19
|
+
.row
|
20
|
+
.col-md-12
|
21
|
+
.panel.panel-default
|
22
|
+
#executing.panel-heading Executing
|
23
|
+
.panel-body
|
24
|
+
- if @auton_state.queue.currently_executing
|
25
|
+
= "%s(%s)" % [@auton_state.queue.currently_executing.name, @auton_state.queue.currently_executing.parameters.join(', ')]
|
26
|
+
|
27
|
+
.row
|
28
|
+
.col-md-6
|
29
|
+
#to_execute.panel.panel-default
|
30
|
+
.panel-heading To Execute
|
31
|
+
.panel-body
|
32
|
+
%ul.list-group
|
33
|
+
- @auton_state.queue.to_execute.each do |to_execute|
|
34
|
+
%li.list-group-item= to_execute.name
|
35
|
+
|
36
|
+
.col-md-6
|
37
|
+
#executed.panel.panel-default
|
38
|
+
.panel-heading Executed
|
39
|
+
.panel-body
|
40
|
+
%ul.list-group
|
41
|
+
- @auton_state.queue.executed.each do |executed|
|
42
|
+
%li.list-group-item= executed.name
|
@@ -0,0 +1,8 @@
|
|
1
|
+
.panel.panel-default
|
2
|
+
.panel-heading Create New Auton
|
3
|
+
.panel-body
|
4
|
+
%form{method: 'POST', action: url '/auton/create', role: 'form'}
|
5
|
+
.form-group
|
6
|
+
%label{for: 'auton_type'}Type
|
7
|
+
%input#auton_type.form-control{type: 'text', placeholder: 'Type', name: 'auton_type'}
|
8
|
+
%button#create.btn.btn-default{type: 'submit'}Create
|
@@ -0,0 +1,35 @@
|
|
1
|
+
.panel.panel-default{'ng-app' => 'nestene'}
|
2
|
+
.panel-body
|
3
|
+
%div#tabs{role: 'tabpanel'}
|
4
|
+
%ul.nav.nav-tabs{role: 'tablist'}
|
5
|
+
%li.active#running_nav{role: 'presentation'}
|
6
|
+
%a.btn.btn-default{href: "#running", 'data-toggle'=>"tab", role: 'tab'} Running Autons
|
7
|
+
%li#create_auton_nav{role: 'presentation'}
|
8
|
+
%a.btn.btn-default{href: "#create_auton", 'data-toggle'=>"tab", role: 'tab'} Create Auton
|
9
|
+
%li#credentials_nav{role: 'presentation'}
|
10
|
+
%a.btn.btn-default{href: "#credentials", 'data-toggle'=>"tab", role: 'tab'} Credentials
|
11
|
+
%div.tab-content
|
12
|
+
%div.tab-pane.active.fade.in#running{'ng-controller' => 'AutonsController'}
|
13
|
+
%ul.list-group#auton_list
|
14
|
+
%li.list-group-item{'ng-repeat' => 'autonName in autons'}
|
15
|
+
%a.btn{'ng-href' => url('/auton/{{autonName}}')} {{autonName}}
|
16
|
+
%div.tab-pane.fade#create_auton{'ng-controller' => 'CreateAutonFormController'}
|
17
|
+
.panel.panel-default
|
18
|
+
.panel-heading Create New Auton
|
19
|
+
.panel-body
|
20
|
+
%form{role: 'form'}
|
21
|
+
.form-group
|
22
|
+
%label{for: 'auton_type'} Auton Type
|
23
|
+
%input#auton_type.form-control{type: 'text', placeholder: 'Type', 'ng-model' => 'autonType', 'required' => true}
|
24
|
+
%button#create.btn.btn-default{type: 'submit', 'ng-click' => 'createAuton()'}Create
|
25
|
+
%div.tab-pane.fade#credentials{'ng-controller' => 'CredentialsController'}
|
26
|
+
.panel.panel-default
|
27
|
+
.panel-heading Current Credentials
|
28
|
+
.panel-body
|
29
|
+
.jsonView
|
30
|
+
%json{child: "credentials", 'default-collapsed'=>"false", type: "object"}
|
31
|
+
%button.btn.btn-default{'ng-click' => 'storeCredentials()'} Save
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
!!!
|
2
|
+
%html
|
3
|
+
%head
|
4
|
+
%title Nestene
|
5
|
+
%meta{:content => "width=device-width, initial-scale=1.0", :name => "viewport"}
|
6
|
+
/ Bootstrap
|
7
|
+
%link{:href => "/vendor/bootstrap/css/bootstrap.min.css", :rel => "stylesheet"}
|
8
|
+
%link{:href => "/vendor/json-editor/styles.css", :rel => "stylesheet"}
|
9
|
+
/ HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries
|
10
|
+
/ WARNING: Respond.js doesn't work if you view the page via file://
|
11
|
+
/[if lt IE 9]
|
12
|
+
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
|
13
|
+
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
|
14
|
+
%body
|
15
|
+
.container
|
16
|
+
/ Static navbar
|
17
|
+
.navbar.navbar-default{:role => "navigation"}
|
18
|
+
.navbar-header
|
19
|
+
%button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", :type => "button"}
|
20
|
+
%span.sr-only Toggle navigation
|
21
|
+
%span.icon-bar
|
22
|
+
%span.icon-bar
|
23
|
+
%span.icon-bar
|
24
|
+
%a.navbar-brand{:href => "/"} Nestene
|
25
|
+
%ol.breadcrumb
|
26
|
+
- (@position || [{link: '/', name: 'Home'}]).each do |crumb|
|
27
|
+
%li
|
28
|
+
%a.btn.btn-default.btn-xs{href: crumb[:link]}=crumb[:name]
|
29
|
+
%div
|
30
|
+
- if @error then
|
31
|
+
.alert.alert-error= @error
|
32
|
+
= yield
|
33
|
+
%script{:src => "/vendor/jquery.min.js"}
|
34
|
+
%script{:src => "/vendor/jquery-ui.min.js"}
|
35
|
+
%script{:src => "/vendor/angular.min.js"}
|
36
|
+
%script{:src => "/vendor/bootstrap/js/bootstrap.min.js"}
|
37
|
+
%script{:src => "/vendor/jquery.bootstrap-growl.min.js"}
|
38
|
+
%script{:src => "/vendor/angular-ui-sortable/sortable.min.js"}
|
39
|
+
%script{:src => "/vendor/json-editor/directives.js"}
|
40
|
+
%script{:src => "/app/application.js"}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
.panel.panel-default
|
2
|
+
.panel-heading Schedule Step for #{@auton_id}
|
3
|
+
.panel-body
|
4
|
+
%form{method: 'POST', action: '/auton/%s/schedule_step' % @auton_id, role: 'form'}
|
5
|
+
.form-group
|
6
|
+
%label{for: 'step_name'}Step Name
|
7
|
+
%select.form-control{name: 'step_name'}
|
8
|
+
- @available_methods.each do |method|
|
9
|
+
%option{value: method.name, selected: @step_name == method.name.to_s}= method.name
|
10
|
+
.form-group
|
11
|
+
%label{for: 'step_name'} Parameters
|
12
|
+
- (0 .. @number_of_params - 1).each do |index|
|
13
|
+
%input.form-control{name: 'parameters[%d]' % index, type: 'text'}
|
14
|
+
%button#schedule.btn.btn-default{type: 'submit'}Schedule
|
data/lib/nestene.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'structure_mapper'
|
2
|
+
require 'securerandom'
|
3
|
+
require 'celluloid'
|
4
|
+
require 'celluloid/autostart'
|
5
|
+
require 'nestene/storage'
|
6
|
+
require 'nestene/actor/delayed_scheduler'
|
7
|
+
require 'nestene/actor/core'
|
8
|
+
require 'nestene/actor/auton_storage'
|
9
|
+
require 'nestene/actor/auton_queue'
|
10
|
+
require 'nestene/auton_context'
|
11
|
+
require 'nestene/callback'
|
12
|
+
require 'nestene/scheduled_method'
|
13
|
+
require 'nestene/executing_method'
|
14
|
+
require 'nestene/execution_error'
|
15
|
+
require 'nestene/executed_method'
|
16
|
+
require 'nestene/auton_execution_queue'
|
17
|
+
require 'nestene/auton_state'
|
18
|
+
require 'nestene/ui/app'
|
19
|
+
|
20
|
+
module Nestene
|
21
|
+
|
22
|
+
class SelfValue
|
23
|
+
def value
|
24
|
+
self
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.class_from_string(name)
|
29
|
+
name.split('::').inject(Object) do |mod, class_name|
|
30
|
+
mod.const_get(class_name) or raise "Could not find class #{name}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.start_nestene(storage)
|
35
|
+
Celluloid::Actor[:nestene_core] = Nestene::Actor::Core.new(storage)
|
36
|
+
Celluloid::Actor[:auton_queue] = Nestene::Actor::AutonQueue.new
|
37
|
+
Celluloid::Actor[:delayed_scheduler] = Nestene::Actor::DelayedScheduler.new
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
class Time
|
43
|
+
def self.from_structure str
|
44
|
+
str ? iso8601(str) : nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_structure
|
48
|
+
iso8601(3)
|
49
|
+
end
|
50
|
+
end
|
data/nestene.gemspec
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'nestene/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "nestene"
|
8
|
+
spec.version = Nestene::VERSION
|
9
|
+
spec.authors = ["Dragan Milic"]
|
10
|
+
spec.email = ["dragan@netice9.com"]
|
11
|
+
spec.summary = %q{Nestene Automation Framework}
|
12
|
+
spec.description = %q{Nestene Automation Framework}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
|
24
|
+
spec.add_development_dependency "cucumber"
|
25
|
+
spec.add_development_dependency "rspec"
|
26
|
+
spec.add_development_dependency "pry"
|
27
|
+
spec.add_development_dependency "pry-debugger"
|
28
|
+
spec.add_development_dependency "timecop"
|
29
|
+
spec.add_development_dependency "capybara"
|
30
|
+
spec.add_development_dependency "rack-test"
|
31
|
+
spec.add_development_dependency "poltergeist"
|
32
|
+
|
33
|
+
spec.add_dependency "sinatra"
|
34
|
+
spec.add_dependency "celluloid"
|
35
|
+
spec.add_dependency "structure_mapper"
|
36
|
+
spec.add_dependency "haml"
|
37
|
+
spec.add_dependency "puma"
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'timecop'
|
3
|
+
|
4
|
+
module Nestene
|
5
|
+
|
6
|
+
|
7
|
+
describe AutonExecutionQueue do
|
8
|
+
|
9
|
+
before do
|
10
|
+
Timecop.freeze(Time.local(1990))
|
11
|
+
end
|
12
|
+
|
13
|
+
after do
|
14
|
+
Timecop.return
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#remove_delayed_method' do
|
18
|
+
context "when the delayed method has been scheduled" do
|
19
|
+
let (:delayed_method_uuid) {subject.add_delayed_method(:some_method, ['p1','p2'], 0.3).uuid}
|
20
|
+
|
21
|
+
it "should remove the delayed method" do
|
22
|
+
uuid = delayed_method_uuid
|
23
|
+
subject.remove_delayed_method(uuid)
|
24
|
+
|
25
|
+
expect(subject.delayed).to be_empty
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
describe '#add_delayed_method' do
|
33
|
+
|
34
|
+
let(:every) { nil }
|
35
|
+
|
36
|
+
before {subject.add_delayed_method :some_method, ['p1','p2'], 0.3, every}
|
37
|
+
|
38
|
+
it "should add a delayed method" do
|
39
|
+
expect(subject.delayed).to_not be_empty
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should assign proper execute_at to delayed method" do
|
43
|
+
expect(subject.delayed.first.execute_at).to eq(Time.now + 0.3)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should assign uuid to delayed method" do
|
47
|
+
expect(subject.delayed.first.uuid).to_not be_nil
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should assign scheduled_at to delayed method" do
|
51
|
+
expect(subject.delayed.first.scheduled_at).to eq(Time.now)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should assign parameters to delayed method" do
|
55
|
+
expect(subject.delayed.first.parameters).to eq(["p1", "p2"])
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
it "should sort delayed methods by their execute_at times in ascending order" do
|
60
|
+
subject.add_delayed_method :sooner_method, ['p1','p2'], 0.1
|
61
|
+
|
62
|
+
expect(subject.delayed.first.name).to eq(:sooner_method)
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when every is not nil" do
|
66
|
+
|
67
|
+
let(:every) { 0.5 }
|
68
|
+
|
69
|
+
it "should set every value" do
|
70
|
+
expect(subject.delayed.first.every).to eq(every)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'timecop'
|
3
|
+
|
4
|
+
module Nestene
|
5
|
+
|
6
|
+
describe ScheduledMethod do
|
7
|
+
describe '#new' do
|
8
|
+
context "when DelayedMethod is passed as parameter" do
|
9
|
+
|
10
|
+
let(:delayed_method) do
|
11
|
+
method = DelayedMethod.new
|
12
|
+
method.name = :some_method
|
13
|
+
method.parameters = ['p1','p2']
|
14
|
+
method.uuid = 'some_uuid'
|
15
|
+
method.scheduled_at = Time.now
|
16
|
+
method
|
17
|
+
end
|
18
|
+
|
19
|
+
subject {described_class.new(delayed_method)}
|
20
|
+
|
21
|
+
|
22
|
+
it "should copy method's uuid" do
|
23
|
+
expect(subject.uuid).to eq(delayed_method.uuid)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should copy name" do
|
27
|
+
expect(subject.name).to eq(delayed_method.name)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should copy parameters" do
|
31
|
+
expect(subject.parameters).to eq(delayed_method.parameters)
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe ExecutingMethod do
|
40
|
+
describe :new do
|
41
|
+
context "when ScheduledMethod is passed as parameter" do
|
42
|
+
let (:callback) {Callback.new}
|
43
|
+
let (:scheduled) do
|
44
|
+
scheduled = ScheduledMethod.new
|
45
|
+
scheduled.callback = callback
|
46
|
+
scheduled
|
47
|
+
end
|
48
|
+
|
49
|
+
subject {ExecutingMethod.new(scheduled)}
|
50
|
+
|
51
|
+
it "should copy callback" do
|
52
|
+
expect(subject.callback).to eq(callback)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
describe ExecutedMethod do
|
60
|
+
describe :new do
|
61
|
+
context "when ExecutingMethod is passed as parameter" do
|
62
|
+
let (:callback) {Callback.new}
|
63
|
+
let (:executed) do
|
64
|
+
executed = ExecutingMethod.new
|
65
|
+
executed.callback = callback
|
66
|
+
executed.started_at = Time.now
|
67
|
+
executed
|
68
|
+
end
|
69
|
+
|
70
|
+
subject {ExecutedMethod.new(executed)}
|
71
|
+
|
72
|
+
it "should copy callback" do
|
73
|
+
expect(subject.callback).to eq(callback)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe ExecutionError do
|
80
|
+
|
81
|
+
let(:exception) do
|
82
|
+
begin
|
83
|
+
raise Exception, "some message"
|
84
|
+
rescue Exception => e
|
85
|
+
e
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "when no error is passed to constructur" do
|
90
|
+
it "should have nil messsage" do
|
91
|
+
expect(subject.message).to be_nil
|
92
|
+
end
|
93
|
+
it "should have nil type" do
|
94
|
+
expect(subject.type).to be_nil
|
95
|
+
end
|
96
|
+
it "should have nil backtrace" do
|
97
|
+
expect(subject.backtrace).to be_nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'when error is passed to constructor' do
|
102
|
+
subject { described_class.new(exception) }
|
103
|
+
|
104
|
+
it "should have message set" do
|
105
|
+
expect(subject.message).to eq('some message')
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should have type set" do
|
109
|
+
expect(subject.type).to eq('Exception')
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should have backtrace set" do
|
113
|
+
expect(subject.backtrace).not_to be_empty
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
describe :to_exception do
|
119
|
+
subject { ExecutionError.new(exception).to_exception }
|
120
|
+
|
121
|
+
it "should create" do
|
122
|
+
expect(subject).to be_a(Exception)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should create exception with the stored messsage" do
|
126
|
+
expect(subject.message).to eq("some message")
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should create exception with a backtrace" do
|
130
|
+
expect(subject.backtrace).not_to be_empty
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Nestene
|
4
|
+
|
5
|
+
describe FileNameConverter do
|
6
|
+
describe '#key_to_file_name' do
|
7
|
+
it "should convert non-charater in the file name to hex-escaped version" do
|
8
|
+
expect(subject.key_to_file_name('test_-+/')).to eq('test_5f_2d_2b_2f')
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#file_name_to_key' do
|
14
|
+
it "should convert hex-escaped version of the file name to the original" do
|
15
|
+
expect(subject.file_name_to_key('test_5f_2d_2b_2f')).to eq('test_-+/')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe DiskStorage do
|
21
|
+
|
22
|
+
let(:tmpdir) {Dir.mktmpdir}
|
23
|
+
|
24
|
+
after{FileUtils.remove_entry_secure tmpdir}
|
25
|
+
|
26
|
+
describe :list do
|
27
|
+
|
28
|
+
subject {DiskStorage.new(tmpdir)}
|
29
|
+
|
30
|
+
context "when there is nothing in the storage" do
|
31
|
+
it "should return empty list" do
|
32
|
+
expect(subject.list).to eq([])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when there is at least one object in the storage" do
|
37
|
+
before {subject.store("test",{})}
|
38
|
+
|
39
|
+
it "should return list of existing objects" do
|
40
|
+
expect(subject.list).to eq(['test'])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe :load do
|
46
|
+
|
47
|
+
subject {DiskStorage.new(tmpdir)}
|
48
|
+
|
49
|
+
context "when the object does not exist" do
|
50
|
+
it "should return nil" do
|
51
|
+
expect(subject.load('whatever')).to be_nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "when the object exists" do
|
56
|
+
before {subject.store('test',{})}
|
57
|
+
it "should return object's structure" do
|
58
|
+
expect(subject.load('test')).to eq({})
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
describe :store do
|
65
|
+
subject {DiskStorage.new(tmpdir)}
|
66
|
+
|
67
|
+
it "stores structure" do
|
68
|
+
subject.store('test',{})
|
69
|
+
expect(subject.load('test')).to eq({})
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
describe MemoryStorage do
|
76
|
+
describe :list do
|
77
|
+
|
78
|
+
subject {MemoryStorage.new}
|
79
|
+
|
80
|
+
context "when there is nothing in the storage" do
|
81
|
+
it "should return empty list" do
|
82
|
+
expect(subject.list).to eq([])
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "when there is at least one object in the storage" do
|
87
|
+
before {subject.store("test",{})}
|
88
|
+
|
89
|
+
it "should return list of existing objects" do
|
90
|
+
expect(subject.list).to eq(['test'])
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe :load do
|
96
|
+
|
97
|
+
subject {MemoryStorage.new}
|
98
|
+
|
99
|
+
context "when the object does not exist" do
|
100
|
+
it "should return nil" do
|
101
|
+
expect(subject.load('whatever')).to be_nil
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "when the object exists" do
|
106
|
+
before {subject.store('test',{})}
|
107
|
+
it "should return object's structure" do
|
108
|
+
expect(subject.load('test')).to eq({})
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
describe :store do
|
116
|
+
subject {MemoryStorage.new}
|
117
|
+
|
118
|
+
it "stores structure" do
|
119
|
+
subject.store('test',{})
|
120
|
+
expect(subject.load('test')).to eq({})
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|
data/spec/spec_helper.rb
ADDED