yolk-memento 0.2.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,181 @@
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
+ describe "on undo" do
26
+ before do
27
+ @states = [@t1 = mock("t1"), @t2 = mock("t2")]
28
+ @session.stub!(:states).and_return(@states)
29
+ end
30
+
31
+ describe "and all states fail" do
32
+ before do
33
+ @t1.stub!(:undo).once().and_return(mock("r1", :success? => false))
34
+ @t2.stub!(:undo).once().and_return(mock("r2", :success? => false))
35
+ @states.stub!(:count).and_return(2)
36
+ end
37
+
38
+ it "should call undo on all states when undo is called" do
39
+ @t1.should_receive(:undo).once().and_return(mock("r1", :success? => false))
40
+ @t2.should_receive(:undo).once().and_return(mock("r2", :success? => false))
41
+ @session.undo
42
+ end
43
+
44
+ it "should kepp itself" do
45
+ @session.undo
46
+ @session.reload
47
+ end
48
+ end
49
+
50
+ describe "and all states succeed" do
51
+ before do
52
+ @t1.stub!(:undo).once().and_return(mock("r1", :success? => true, :state => @t1))
53
+ @t2.stub!(:undo).once().and_return(mock("r2", :success? => true, :state => @t2))
54
+ @t1.stub!(:destroy).once()
55
+ @t2.stub!(:destroy).once()
56
+ @states.stub!(:count).and_return(0)
57
+ end
58
+
59
+ it "should destroy itself" do
60
+ @session.undo
61
+ Memento::Session.find_by_id(@session.id).should be_nil
62
+ end
63
+
64
+ it "should destroy all states" do
65
+ @t1.should_receive(:destroy).once()
66
+ @t2.should_receive(:destroy).once()
67
+ @session.undo
68
+ end
69
+ end
70
+
71
+ describe "and some states succeed, some fail" do
72
+ before do
73
+ @t1.stub!(:undo).once().and_return(mock("r1", :success? => true, :state => @t1))
74
+ @t2.stub!(:undo).once().and_return(mock("r2", :success? => false, :state => @t2))
75
+ @t1.stub!(:destroy).once()
76
+ @states.stub!(:count).and_return(1)
77
+ end
78
+
79
+ it "should kepp itself" do
80
+ @session.undo
81
+ @session.reload
82
+ end
83
+
84
+ it "should destroy only successful states" do
85
+ @t1.should_receive(:destroy).once()
86
+ @t2.should_receive(:destroy).never()
87
+ @session.undo
88
+ end
89
+ end
90
+ end
91
+
92
+ describe "on undo!" do
93
+ before do
94
+ @state1 = @session.states.create!(:action_type => "update", :record => @p1 = Project.create!)
95
+ Memento::Session.create!(:user => @user).states.create!(:action_type => "destroy", :record => Project.create!)
96
+ @state2 = @session.states.create!(:action_type => "update", :record => @p2 = Project.create!)
97
+ end
98
+
99
+ describe "and all states succeed" do
100
+ it "should return ResultsArray" do
101
+ @session.undo!.should be_a(Memento::ResultArray)
102
+ end
103
+
104
+ it "should remove all states" do
105
+ @session.undo!
106
+ Memento::State.count.should eql(1)
107
+ end
108
+
109
+ it "should remove itself" do
110
+ @session.undo!
111
+ Memento::Session.find_by_id(@session.id).should be_nil
112
+ end
113
+ end
114
+
115
+ describe "and all states fail" do
116
+ before do
117
+ @state1.update_attribute(:record_data, {:name => ["A", "B"]})
118
+ @p1.update_attribute(:name, "C")
119
+ @state2.update_attribute(:record_data, {:name => ["A", "B"]})
120
+ @p2.update_attribute(:name, "C")
121
+ end
122
+
123
+ it "should keep all states" do
124
+ @session.undo! rescue
125
+ Memento::State.count.should eql(3)
126
+ end
127
+
128
+ it "should keep itself" do
129
+ @session.undo! rescue
130
+ @session.reload
131
+ end
132
+
133
+ it "should raise Memento::ErrorOnRewind" do
134
+ lambda{ @session.undo! }.should raise_error(Memento::ErrorOnRewind)
135
+ end
136
+ end
137
+
138
+ describe "and some states succeed, some fail" do
139
+ before do
140
+ @state1.update_attribute(:record_data, {:name => ["A", "B"]})
141
+ @p1.update_attribute(:name, "C")
142
+ end
143
+
144
+ it "should keep all states" do
145
+ @session.undo! rescue nil
146
+ Memento::State.count.should eql(3)
147
+ end
148
+
149
+ it "should keep itself" do
150
+ @session.undo! rescue nil
151
+ @session.reload
152
+ end
153
+
154
+ it "should raise Memento::ErrorOnRewind" do
155
+ lambda{ @session.undo! }.should raise_error(Memento::ErrorOnRewind)
156
+ end
157
+ end
158
+ end
159
+
160
+ describe "with states" do
161
+ before do
162
+ @session.states.create!(:action_type => "destroy", :record => Project.create!)
163
+ Memento::Session.create!(:user => @user).states.create!(:action_type => "destroy", :record => Project.create!)
164
+ @state2 = @session.states.create!(:action_type => "update", :record => Project.create!)
165
+ end
166
+
167
+ it "should destroy all states when destroyed" do
168
+ Memento::State.count.should eql(3)
169
+ @session.destroy
170
+ Memento::State.count.should eql(1)
171
+ end
172
+
173
+ end
174
+
175
+
176
+
177
+ after do
178
+ shutdown_db
179
+ end
180
+
181
+ end
@@ -0,0 +1,105 @@
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
+
50
+ it "should give back new unsaved copy of object on new_object" do
51
+ @state.new_object.should be_kind_of(Project)
52
+ @state.new_object.name.should be_nil
53
+ @state.new_object do |object|
54
+ object.name = "B"
55
+ end.name.should eql("B")
56
+ end
57
+
58
+ it "should give back new unsaved copy filled with old data of object on rebuild_object" do
59
+ @state.rebuild_object.should be_kind_of(Project)
60
+ @state.rebuild_object.name.should eql("A")
61
+ @state.rebuild_object(:id).id.should be_nil # skip id
62
+ @state.rebuild_object.id.should eql(@project.id)
63
+ @state.rebuild_object do |object|
64
+ object.name = "B"
65
+ end.name.should eql("B")
66
+ end
67
+
68
+ describe "on later_states_on_record_for" do
69
+
70
+ it "should return empty array" do
71
+ @state.later_states_on_record_for("destroy").should eql([])
72
+ end
73
+
74
+ it "should return filled array when other record of the given action_type exists" do
75
+ Memento::Session.create!(:user => @user).states.create!(:action_type => "destroy", :record => @project )
76
+ @state.later_states_on_record_for("destroy").map(&:class).should eql([Memento::State])
77
+ @state.later_states_on_record_for(:"destroy").map(&:id).should eql([2])
78
+ end
79
+
80
+ it "should return empty array when only records of another action_type exists" do
81
+ Memento::Session.create!(:user => @user).states.create!(:action_type => "update", :record => @project )
82
+ @state.later_states_on_record_for("destroy").should eql([])
83
+ end
84
+
85
+ it "should return empty array when only destroy records of another record exists" do
86
+ Memento::Session.create!(:user => @user).states.create!(:action_type => "destroy", :record => Project.create(:name => "B") )
87
+ @state.later_states_on_record_for("destroy").should eql([])
88
+ end
89
+
90
+ it "should return empty array when only destroy records older than @tack exist" do
91
+ state2 = Memento::Session.create!(:user => @user).states.create!(:action_type => "destroy", :record => @project )
92
+ state2.update_attribute(:created_at, 3.minutes.ago)
93
+ @state.later_states_on_record_for("destroy").should eql([])
94
+ end
95
+ end
96
+
97
+
98
+
99
+ end
100
+
101
+ after do
102
+ shutdown_db
103
+ end
104
+
105
+ end