memento 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,55 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ class FooController < ActionController::Base
4
+
5
+ private
6
+
7
+ end
8
+
9
+ describe Memento::ActionControllerMethods do
10
+
11
+ before do
12
+ setup_db
13
+ setup_data
14
+ @controller = FooController.new
15
+ @controller.stub!(:current_user).and_return(@user)
16
+ @headers = {}
17
+ @controller.stub!(:response).and_return(mock("response", :headers => @headers))
18
+ end
19
+
20
+ after do
21
+ shutdown_db
22
+ end
23
+
24
+ it "should add memento-method to ActionController::Base" do
25
+ FooController.private_instance_methods.should include("memento")
26
+ end
27
+
28
+ it "should call memento#memento with user and block" do
29
+ project = Project.create!
30
+ @controller.send(:memento) do
31
+ project.update_attribute(:name, "P7")
32
+ end
33
+ project.reload.name.should eql("P7")
34
+ project.memento_states.count.should eql(1)
35
+ Memento::Session.count.should eql(1)
36
+ end
37
+
38
+ it "should set header X-MementoSessionId" do
39
+ @controller.send(:memento) { Project.create!.update_attribute(:name, "P7") }
40
+ @headers.should == {'X-Memento-Session-Id' => Memento::Session.last.id.to_s }
41
+ end
42
+
43
+ it "should return result of given block" do
44
+ @controller.send(:memento) do
45
+ 1 + 2
46
+ end.should eql(3)
47
+ end
48
+
49
+ it "should not set header when no session stored" do
50
+ @controller.send(:memento) { Customer.create! } # not stored
51
+ @headers['X-MementoSessionId'].should be_nil
52
+ @headers.should_not have_key('X-Memento-Session-Id')
53
+ end
54
+
55
+ end
@@ -0,0 +1,65 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe Memento::ActiveRecordMethods do
4
+
5
+ before do
6
+ setup_db
7
+ setup_data
8
+ end
9
+
10
+ it "should declare private methods on Project" do
11
+ Project.private_instance_methods.should include("record_destroy", "record_update", "record_create")
12
+ end
13
+
14
+ it "should set hook on create to call Memento" do
15
+ project = Project.new(:name => "Project X")
16
+ Memento.instance.should_receive(:add_state).once().with("create", project)
17
+ project.save!
18
+ end
19
+
20
+ it "should set hook on update to call Memento" do
21
+ project = Project.create!(:name => "Project X")
22
+ Memento.instance.should_receive(:add_state).once().with("update", project)
23
+ project.update_attribute(:name, "Project XY")
24
+ end
25
+
26
+ it "should set hook on destroy to call Memento" do
27
+ project = Project.create!(:name => "Project X")
28
+ Memento.instance.should_receive(:add_state).once().with("destroy", project)
29
+ project.destroy
30
+ end
31
+
32
+ it "should define attributes_for_memento and ignore attributes given by options" do
33
+ Project.create!(:name => "Project X").attributes_for_memento.should == {
34
+ "id"=>1, "name"=>"Project X", "notes"=>nil, "customer_id"=>nil, "closed_at"=>nil
35
+ }
36
+ end
37
+
38
+ it "should define changes_for_memento and ignore attributes given by options" do
39
+ project = Project.create!(:name => "Project X")
40
+ project.name = "A Project"
41
+ project.updated_at = 5.minutes.ago
42
+ project.notes = "new"
43
+ project.ignore_this = 2
44
+ project.changes_for_memento.should_not == project.changes
45
+ project.changes_for_memento.should == {"name"=>["Project X", "A Project"], "notes"=>[nil, "new"]}
46
+ end
47
+
48
+ it "should define has_many association to memento_states" do
49
+ project = Project.create!(:name => "Project X")
50
+ project.memento_states.should be_empty
51
+ Memento.instance.memento(@user) { project.update_attribute(:name, "Project Y") }
52
+ project.memento_states.count.should eql(1)
53
+ Memento.instance.memento(@user) { project.update_attribute(:name, "Project Y") }
54
+ project.memento_states.count.should eql(1)
55
+ Memento.instance.memento(@user) { Project.create!.update_attribute(:name, "Project X") }
56
+ project.memento_states.count.should eql(1)
57
+ Project.last.memento_states.count.should eql(2)
58
+ Memento::State.count.should eql(3)
59
+ end
60
+
61
+ after do
62
+ shutdown_db
63
+ end
64
+
65
+ end
@@ -0,0 +1,89 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe Memento::Result do
4
+
5
+ describe "when initalized with valid object" do
6
+ before do
7
+ @object = mock("object", :errors => {})
8
+ @state = mock("state1")
9
+ @result = Memento::Result.new(@object, @state)
10
+ end
11
+
12
+ it "should have an object attribute" do
13
+ @result.object.should eql(@object)
14
+ end
15
+
16
+ it "should have an state attribute" do
17
+ @result.state.should eql(@state)
18
+ end
19
+
20
+ it "should have an error attribute" do
21
+ @result.error.should be_nil
22
+ end
23
+
24
+ it "should be valid" do
25
+ @result.should be_success
26
+ @result.should_not be_failed
27
+ end
28
+ end
29
+
30
+ describe "when initalized with object with errors" do
31
+ before do
32
+ @object = mock("object", :errors => {:memento_undo => "123"})
33
+ @result = Memento::Result.new(@object, mock("state1"))
34
+ end
35
+
36
+ it "should have an object attribute" do
37
+ @result.object.should eql(@object)
38
+ end
39
+
40
+ it "should return error" do
41
+ @result.error.should eql("123")
42
+ end
43
+
44
+ it "should be invalid" do
45
+ @result.should be_failed
46
+ @result.should_not be_success
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ describe Memento::ResultArray do
53
+
54
+ before do
55
+ @results = Memento::ResultArray.new()
56
+ end
57
+
58
+ it "should have an empty errors array" do
59
+ @results.errors.should eql([])
60
+ end
61
+
62
+ it "should have no errors" do
63
+ @results.should be_success
64
+ @results.should_not be_failed
65
+ end
66
+
67
+ describe "when Memento::Result without errors added" do
68
+ before do
69
+ @object = mock("object", :errors => {:memento_undo => "123"})
70
+ @results << Memento::Result.new(mock("object2", :errors => {}), mock("state1"))
71
+ @results << (@with_error = Memento::Result.new(@object, mock("state2")))
72
+ end
73
+
74
+ it "should have two entrys" do
75
+ @results.size.should eql(2)
76
+ end
77
+
78
+ it "should have one error" do
79
+ @results.errors.size.should eql(1)
80
+ @results.errors.should eql([@with_error])
81
+ end
82
+
83
+ it "should have an error" do
84
+ @results.should_not be_success
85
+ @results.should be_failed
86
+ end
87
+ end
88
+
89
+ end
@@ -0,0 +1,117 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe Memento::Session do
4
+
5
+ before do
6
+ setup_db
7
+ setup_data
8
+ @session = Memento::Session.create(:user => @user)
9
+ end
10
+
11
+ it "should belong to user" do
12
+ @session.user.should eql(@user)
13
+ end
14
+
15
+ it "should require user" do
16
+ Memento::Session.create.errors[:user].should eql("can't be blank")
17
+ end
18
+
19
+ it "should have_many states" do
20
+ @session.states.should eql([])
21
+ @session.states.create!(:action_type => "destroy", :record => Project.create!)
22
+ @session.states.count.should eql(1)
23
+ end
24
+
25
+ context "undo" do
26
+ before do
27
+ @state1 = @session.states.create!(:action_type => "update", :record => @p1 = Project.create!)
28
+ @other = Memento::Session.create!(:user => @user).states.create!(:action_type => "destroy", :record => Project.create!)
29
+ @state2 = @session.states.create!(:action_type => "update", :record => @p2 = Project.create!)
30
+ end
31
+
32
+ describe "and all states succeed" do
33
+ it "should return ResultsArray" do
34
+ @session.undo.should be_a(Memento::ResultArray)
35
+ end
36
+
37
+ it "should remove all states" do
38
+ @session.undo
39
+ Memento::State.count.should eql(1)
40
+ end
41
+
42
+ it "should remove itself" do
43
+ @session.undo
44
+ Memento::Session.find_by_id(@session.id).should be_nil
45
+ end
46
+ end
47
+
48
+ describe "and all states fail" do
49
+ before do
50
+ @state1.update_attribute(:record_data, {:name => ["A", "B"]})
51
+ @p1.update_attribute(:name, "C")
52
+ @state2.update_attribute(:record_data, {:name => ["A", "B"]})
53
+ @p2.update_attribute(:name, "C")
54
+ end
55
+
56
+ it "should keep all states" do
57
+ @session.undo
58
+ Memento::State.count.should eql(3)
59
+ end
60
+
61
+ it "should keep itself" do
62
+ @session.undo
63
+ @session.reload
64
+ end
65
+
66
+ it "should raise Memento::ErrorOnRewind on undo!" do
67
+ lambda{ @session.undo! }.should raise_error(Memento::ErrorOnRewind)
68
+ end
69
+ end
70
+
71
+ describe "and some states succeed, some fail" do
72
+ before do
73
+ @state1.update_attribute(:record_data, {:name => ["A", "B"]})
74
+ @p1.update_attribute(:name, "C")
75
+ end
76
+
77
+ it "should keep all states when using undo!" do
78
+ @session.undo! rescue
79
+ Memento::State.count.should eql(3)
80
+ end
81
+
82
+ it "should NOT keep all states when using undo" do
83
+ @session.undo
84
+ Memento::State.count.should eql(2)
85
+ end
86
+
87
+ it "should keep itself" do
88
+ @session.undo rescue nil
89
+ @session.reload
90
+ end
91
+
92
+ it "should raise Memento::ErrorOnRewind on undo!" do
93
+ lambda{ @session.undo! }.should raise_error(Memento::ErrorOnRewind)
94
+ end
95
+ end
96
+ end
97
+
98
+ describe "with states" do
99
+ before do
100
+ @session.states.create!(:action_type => "destroy", :record => Project.create!)
101
+ Memento::Session.create!(:user => @user).states.create!(:action_type => "destroy", :record => Project.create!)
102
+ @state2 = @session.states.create!(:action_type => "update", :record => Project.create!)
103
+ end
104
+
105
+ it "should destroy all states when destroyed" do
106
+ Memento::State.count.should eql(3)
107
+ @session.destroy
108
+ Memento::State.count.should eql(1)
109
+ end
110
+
111
+ end
112
+
113
+ after do
114
+ shutdown_db
115
+ end
116
+
117
+ end
@@ -0,0 +1,55 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe Memento::State do
4
+
5
+ before do
6
+ setup_db
7
+ setup_data
8
+ @session = Memento::Session.create(:user => @user)
9
+ end
10
+
11
+ it "should belong to session" do
12
+ Memento::State.new(:session => @session).session.should eql(@session)
13
+ end
14
+
15
+ it "should require session" do
16
+ Memento::State.create.errors[:session].should eql("can't be blank")
17
+ end
18
+
19
+ it "should require action_type to be one of Memento::State::RECORD_CAUSES" do
20
+ Memento::State.create.errors[:action_type].should eql("can't be blank")
21
+ Memento::State.create(:action_type => "move").errors[:action_type].should eql("is not included in the list")
22
+ end
23
+
24
+ it "should belong to polymorphic record" do
25
+ Memento::State.new(:record => @user).record.should eql(@user)
26
+ Memento::State.new(:record => @session).record.should eql(@session)
27
+ end
28
+
29
+ it "should require record" do
30
+ Memento::State.create.errors[:record].should eql("can't be blank")
31
+ end
32
+
33
+
34
+ describe "valid State" do
35
+ before do
36
+ @state = @session.states.create!(:action_type => "destroy", :record => @project = Project.create(:name => "A") )
37
+ end
38
+
39
+ it "should give back Memento::Result on undo" do
40
+ result = @state.undo
41
+ result.should be_a(Memento::Result)
42
+ result.object.should be_a(Project)
43
+ result.state.should eql(@state)
44
+ end
45
+
46
+ it "should give back old data on record_data" do
47
+ @state.record_data.should == (@project.attributes_for_memento)
48
+ end
49
+ end
50
+
51
+ after do
52
+ shutdown_db
53
+ end
54
+
55
+ end
@@ -0,0 +1,169 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Memento do
4
+ before do
5
+ setup_db
6
+ setup_data
7
+ end
8
+
9
+ after do
10
+ shutdown_db
11
+ end
12
+
13
+ it "should be a singleton" do
14
+ Memento.instance.should be_kind_of(Singleton)
15
+ Memento.instance.should eql(Memento.instance)
16
+ lambda{ Memento.new }.should raise_error(NoMethodError)
17
+ end
18
+
19
+ it "should not be memento by default" do
20
+ Memento.instance.should_not be_active
21
+ end
22
+
23
+ describe "start" do
24
+
25
+ before do
26
+ Memento.instance.start(@user)
27
+ @session = Memento.instance.instance_variable_get("@session")
28
+ end
29
+
30
+ it "should require user or user_id on start" do
31
+ lambda{ Memento.instance.start }.should raise_error(ArgumentError)
32
+ end
33
+
34
+ it "should set an unsaved memento_session when starting" do
35
+ Memento::Session.count.should eql(0)
36
+ @session.should be_kind_of(Memento::Session)
37
+ @session.should be_new_record
38
+ end
39
+
40
+ it "should set user on session" do
41
+ @session.user.should eql(User.first)
42
+ end
43
+
44
+ it "should set user when passing in id as integer" do
45
+ Memento.instance.start(User.create(:name => "MyUser2").id)
46
+ Memento.instance.instance_variable_get("@session").user.should eql(User.last)
47
+ end
48
+
49
+ it "should not start memento when user does not exists/is invalid" do
50
+ Memento.instance.stop
51
+ Memento.instance.start(123333)
52
+ Memento.instance.should_not be_active
53
+ Memento.instance.start("123")
54
+ Memento.instance.should_not be_active
55
+ end
56
+
57
+ it "should be memento" do
58
+ Memento.instance.should be_active
59
+ end
60
+
61
+ end
62
+
63
+ describe "stop" do
64
+ before do
65
+ Memento.instance.start(@user)
66
+ Memento.instance.stop
67
+ end
68
+
69
+ it "should not be memento" do
70
+ Memento.instance.should_not be_active
71
+ end
72
+
73
+ it "should remove session if no states created" do
74
+ Memento::Session.count.should eql(0)
75
+ end
76
+ end
77
+
78
+ describe "memento block" do
79
+
80
+ it "should record inside of block and stop after" do
81
+ Memento.instance.should_not be_active
82
+ Memento.instance.memento(@user) do
83
+ Memento.instance.should be_active
84
+ end
85
+ Memento.instance.should_not be_active
86
+ end
87
+
88
+ it "should give back session when states created" do
89
+ Memento.instance.memento(@user) do
90
+ Project.create!
91
+ end.should be_a(Memento::Session)
92
+ end
93
+
94
+ it "should give back false when no states created" do
95
+ Memento.instance.memento(@user) do
96
+ 1 + 1
97
+ end.should be_false
98
+ end
99
+
100
+ it "should raise error in block and stop session" do
101
+ lambda {
102
+ Memento.instance.memento(@user) do
103
+ raise StandardError
104
+ end.should be_nil
105
+ }.should raise_error(StandardError)
106
+ Memento.instance.should_not be_active
107
+ end
108
+
109
+ end
110
+
111
+ describe "when active" do
112
+ before do
113
+ @project = Project.create(:name => "P1")
114
+ Memento.instance.start(@user)
115
+ end
116
+
117
+ after do
118
+ Memento.instance.stop
119
+ end
120
+
121
+ it "should create memento_state for ar-object with action_type" do
122
+ Memento::State.count.should eql(0)
123
+ Memento.instance.add_state :destroy, @project
124
+ Memento::State.count.should eql(1)
125
+ Memento::State.first.action_type.should eql("destroy")
126
+ Memento::State.first.record.should eql(Project.last)
127
+ end
128
+
129
+ it "should save session on first added state" do
130
+ Memento::Session.count.should eql(0)
131
+ Memento.instance.add_state :destroy, @project
132
+ Memento::Session.count.should eql(1)
133
+ end
134
+
135
+ describe "when ignoring" do
136
+ it "should NOT create memento_state for ar-object with action_type" do
137
+ Memento.instance.ignore do
138
+ Memento.instance.add_state :destroy, Project.create(:name => "P1")
139
+ end
140
+
141
+ Memento::State.count.should eql(0)
142
+ end
143
+ end
144
+
145
+ end
146
+
147
+ describe "when not active" do
148
+
149
+ it "should NOT create memento_state for ar-object with action_type" do
150
+ Memento.instance.add_state :destroy, Project.create(:name => "P1")
151
+ Memento::State.count.should eql(0)
152
+ end
153
+
154
+ end
155
+
156
+ context "serializer" do
157
+
158
+ it "should default to yaml" do
159
+ Memento.serializer.should eql(YAML)
160
+ end
161
+
162
+ it "should be changeable" do
163
+ Memento.serializer = Marshal
164
+ Memento.serializer.should eql(Marshal)
165
+ end
166
+
167
+ end
168
+
169
+ end