kasket 3.1.5 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,49 +0,0 @@
1
- require File.expand_path("helper", File.dirname(__FILE__))
2
-
3
- class CacheExpiryTest < ActiveSupport::TestCase
4
- fixtures :blogs, :posts
5
-
6
- context "a cached object" do
7
- setup do
8
- post = Post.first
9
- @post = Post.find(post.id)
10
-
11
- assert(Kasket.cache.read(@post.kasket_key))
12
- end
13
-
14
- should "be removed from cache when deleted" do
15
- @post.destroy
16
- assert_nil(Kasket.cache.read(@post.kasket_key))
17
- end
18
-
19
- should "clear all indices for instance when deleted" do
20
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "id=#{@post.id}")
21
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "title='#{@post.title}'")
22
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "title='#{@post.title}'/first")
23
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "blog_id=#{@post.blog_id}/id=#{@post.id}")
24
- Kasket.cache.expects(:delete).never
25
-
26
- @post.destroy
27
- end
28
-
29
- should "be removed from cache when updated" do
30
- @post.title = "new_title"
31
- @post.save
32
- assert_nil(Kasket.cache.read(@post.kasket_key))
33
- end
34
-
35
- should "clear all indices for instance when updated" do
36
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "id=#{@post.id}")
37
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "title='#{@post.title}'")
38
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "title='#{@post.title}'/first")
39
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "title='new_title'")
40
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "title='new_title'/first")
41
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "blog_id=#{@post.blog_id}/id=#{@post.id}")
42
- Kasket.cache.expects(:delete).never
43
-
44
- @post.title = "new_title"
45
- @post.save
46
- end
47
-
48
- end
49
- end
@@ -1,58 +0,0 @@
1
- require File.expand_path("helper", File.dirname(__FILE__))
2
-
3
- class CacheableTest < ActiveSupport::TestCase
4
- context "#store_in_kasket" do
5
- should "only cache object that are kasket_cacheable?" do
6
- post = Post.send(:instantiate, { 'id' => 1, 'title' => 'Hello' })
7
-
8
- post.expects(:kasket_cacheable?).returns(true)
9
- Kasket.cache.expects(:write).once
10
- post.store_in_kasket
11
-
12
- post.expects(:kasket_cacheable?).returns(false)
13
- Kasket.cache.expects(:write).never
14
- post.store_in_kasket
15
- end
16
- end
17
-
18
- context "caching of results of find" do
19
- setup do
20
- @post_database_result = { 'id' => 1, 'title' => 'Hello' }
21
- @post_records = [Post.send(:instantiate, @post_database_result)]
22
- Post.stubs(:find_by_sql_without_kasket).returns(@post_records)
23
-
24
- @comment_database_result = [{ 'id' => 1, 'body' => 'Hello' }, { 'id' => 2, 'body' => 'World' }]
25
- @comment_records = @comment_database_result.map {|r| Comment.send(:instantiate, r)}
26
- Comment.stubs(:find_by_sql_without_kasket).returns(@comment_records)
27
- end
28
-
29
- context "with just one result" do
30
- should "write result in cache if it is kasket_cacheable?" do
31
- Post.any_instance.expects(:kasket_cacheable?).returns(true)
32
- Kasket.cache.expects(:write).once
33
- Post.find(1)
34
- end
35
-
36
- should "not write result in cache if it is not kasket_cacheable?" do
37
- Post.any_instance.expects(:kasket_cacheable?).returns(false)
38
- Kasket.cache.expects(:write).never
39
- Post.find(1)
40
- end
41
- end
42
-
43
- context "with several results" do
44
- should "write result in cache if all results are kasket_cacheable?" do
45
- Comment.any_instance.stubs(:kasket_cacheable?).returns(true)
46
- Kasket.cache.expects(:write).times(@comment_records.size + 1)
47
- Comment.all(:conditions => {:post_id => 1})
48
- end
49
-
50
- should "not write result in cache if any of them are not kasket_cacheable?" do
51
- @comment_records[0].expects(:kasket_cacheable?).returns(true)
52
- @comment_records[1].expects(:kasket_cacheable?).returns(false)
53
- Kasket.cache.expects(:write).never
54
- Comment.all(:conditions => {:post_id => 1})
55
- end
56
- end
57
- end
58
- end
@@ -1,44 +0,0 @@
1
- require File.expand_path("helper", File.dirname(__FILE__))
2
- require "digest/md5"
3
-
4
- class ConfigurationMixinTest < ActiveSupport::TestCase
5
- context "Generating cache keys" do
6
- should "not choke on empty numeric attributes" do
7
- expected_cache_key = "#{Post.kasket_key_prefix}blog_id=null"
8
- query_attributes = [ [:blog_id, ''] ]
9
-
10
- assert_equal expected_cache_key, Post.kasket_key_for(query_attributes)
11
- end
12
-
13
- should "not fail on unknown columns" do
14
- expected_cache_key = "#{Post.kasket_key_prefix}does_not_exist=111"
15
- query_attributes = [ [:does_not_exist, '111'] ]
16
-
17
- assert_equal expected_cache_key, Post.kasket_key_for(query_attributes)
18
- end
19
-
20
- should "not generate keys longer that 255" do
21
- very_large_number = (1..999).to_a.join
22
- query_attributes = [ [:blog_id, very_large_number] ]
23
-
24
- assert(Post.kasket_key_for(query_attributes).size < 255)
25
- end
26
-
27
- should "not generate keys with spaces" do
28
- query_attributes = [ [:title, 'this key has speces'] ]
29
-
30
- assert(!(Post.kasket_key_for(query_attributes) =~ /\s/))
31
- end
32
-
33
- should "downcase string attributes" do
34
- query_attributes = [ [:title, 'ThIs'] ]
35
- expected_cache_key = "#{Post.kasket_key_prefix}title='this'"
36
-
37
- assert_equal expected_cache_key, Post.kasket_key_for(query_attributes)
38
- end
39
-
40
- should "build correct prefix" do
41
- assert_equal "kasket-#{Kasket::Version::PROTOCOL}/R#{ActiveRecord::VERSION::MAJOR}#{ActiveRecord::VERSION::MINOR}/posts/version=#{POST_VERSION}/", Post.kasket_key_prefix
42
- end
43
- end
44
- end
@@ -1,7 +0,0 @@
1
- test:
2
- adapter: mysql
3
- encoding: utf8
4
- database: kasket_test
5
- username: root
6
- password:
7
- host: 127.0.0.1
@@ -1,16 +0,0 @@
1
- require File.expand_path("helper", File.dirname(__FILE__))
2
-
3
- class DirtyTest < ActiveSupport::TestCase
4
- fixtures :blogs, :posts
5
-
6
- should "clear the indices when a dirty method is called" do
7
- post = Post.first
8
-
9
- Post.cache { pots = Post.find(post.id) }
10
- assert(Kasket.cache.read(post.kasket_key))
11
-
12
- post.make_dirty!
13
-
14
- assert_nil(Kasket.cache.read(post.kasket_key))
15
- end
16
- end
@@ -1,61 +0,0 @@
1
- require File.expand_path("helper", File.dirname(__FILE__))
2
-
3
- class FindOneTest < ActiveSupport::TestCase
4
- fixtures :blogs, :posts
5
-
6
- should "cache find(id) calls" do
7
- post = Post.first
8
- Kasket.cache.write(post.kasket_key, nil)
9
- assert_equal(post, Post.find(post.id))
10
- assert(Kasket.cache.read(post.kasket_key))
11
- Post.connection.expects(:select_all).never
12
- assert_equal(post, Post.find(post.id))
13
- end
14
-
15
- should "only cache on indexed attributes" do
16
- Kasket.cache.expects(:read).twice
17
- Post.find_by_id(1)
18
- Post.find_by_id(1, :conditions => {:blog_id => 2})
19
-
20
- Kasket.cache.expects(:read).never
21
- Post.first :conditions => {:blog_id => 2}
22
- end
23
-
24
- should "not use cache when using the :select option" do
25
- post = Post.first
26
- assert_nil(Kasket.cache.read(post.kasket_key))
27
-
28
- Post.find(post.id, :select => 'title')
29
- assert_nil(Kasket.cache.read(post.kasket_key))
30
-
31
- Post.find(post.id)
32
- assert(Kasket.cache.read(post.kasket_key))
33
-
34
- Kasket.cache.expects(:read)
35
- Post.find(post.id, :select => nil)
36
-
37
- Kasket.cache.expects(:read).never
38
- Post.find(post.id, :select => 'title')
39
- end
40
-
41
- should "respect scope" do
42
- post = Post.find(Post.first.id)
43
- other_blog = Blog.first(:conditions => "id != #{post.blog_id}")
44
-
45
- assert(Kasket.cache.read(post.kasket_key))
46
-
47
- assert_raise(ActiveRecord::RecordNotFound) do
48
- other_blog.posts.find(post.id)
49
- end
50
- end
51
-
52
- should "use same scope when finding on has_many" do
53
- post = Blog.first.posts.first
54
- blog = Blog.first
55
- Rails.cache.clear
56
-
57
- post = blog.posts.find_by_id(post.id)
58
- key = post.kasket_key.sub(%r{(/id=#{post.id})}, "/blog_id=#{Blog.first.id}\\1")
59
- assert(Kasket.cache.read(key))
60
- end
61
- end
@@ -1,55 +0,0 @@
1
- require File.expand_path("helper", File.dirname(__FILE__))
2
-
3
- class FindSomeTest < ActiveSupport::TestCase
4
- fixtures :blogs, :posts
5
-
6
- def setup
7
- @post1 = Post.first
8
- @post2 = Post.last
9
- Post.find(@post1.id, @post2.id)
10
- assert Kasket.cache.read(@post1.kasket_key)
11
- assert Kasket.cache.read(@post2.kasket_key)
12
- end
13
-
14
- should "use cache for find(id, id) calls" do
15
- Post.connection.expects(:select_all).never
16
- Post.find(@post1.id, @post2.id)
17
- end
18
-
19
- should "cache when found using find(id, id) calls" do
20
- Kasket.cache.delete(@post1.kasket_key)
21
- Kasket.cache.delete(@post2.kasket_key)
22
-
23
- Post.find(@post1.id, @post2.id)
24
-
25
- assert Kasket.cache.read(@post1.kasket_key)
26
- assert Kasket.cache.read(@post2.kasket_key)
27
- end
28
-
29
- should "only lookup the records that are not in the cache" do
30
- Kasket.cache.delete(@post2.kasket_key)
31
-
32
- # has to lookup post2 via db
33
- Post.expects(:find_by_sql_without_kasket).returns([@post2])
34
- found_posts = Post.find(@post1.id, @post2.id)
35
- assert_equal [@post1, @post2].map(&:id).sort, found_posts.map(&:id).sort
36
-
37
- # now all are cached
38
- Post.expects(:find_by_sql_without_kasket).never
39
- found_posts = Post.find(@post1.id, @post2.id)
40
- assert_equal [@post1, @post2].map(&:id).sort, found_posts.map(&:id).sort
41
- end
42
-
43
- context "unfound" do
44
- should "ignore unfound when using find_all_by_id" do
45
- found_posts = Post.find_all_by_id([@post1.id, 1231232])
46
- assert_equal [@post1.id], found_posts.map(&:id)
47
- end
48
-
49
- should "not ignore unfound when using find" do
50
- assert_raise ActiveRecord::RecordNotFound do
51
- Post.find(@post1.id, 1231232)
52
- end
53
- end
54
- end
55
- end
@@ -1,7 +0,0 @@
1
- mick:
2
- name: Mick Staugaard
3
- metadata:
4
- sex: male
5
-
6
- eric:
7
- name: Eric Chapweske
@@ -1,5 +0,0 @@
1
- a_blog:
2
- name: My Blog
3
-
4
- other_blog:
5
- name: Some Other Blog
@@ -1,16 +0,0 @@
1
- few_comments_1:
2
- id: 1
3
- post_id: 2
4
- body: what ever body 1
5
-
6
- few_comments_2:
7
- id: 2
8
- post_id: 2
9
- body: what ever body 2
10
-
11
- <% (1..10).each do |i| %>
12
- many_comments_<%= i %>:
13
- post: has_many_comments
14
- body: what ever body <%= i %>
15
- <% end %>
16
-
@@ -1,24 +0,0 @@
1
- no_comments:
2
- id: 1
3
- blog: a_blog
4
- title: no_comments
5
- author: mick
6
- created_at: 2013-10-14 15:30:00
7
-
8
- has_two_comments:
9
- id: 2
10
- blog: a_blog
11
- title: few_comments
12
- author: mick
13
-
14
- on_other_blog:
15
- id: 3
16
- blog: other_blog
17
- title: no_comments
18
- author: eric
19
-
20
- has_many_comments:
21
- id: 4
22
- blog: a_blog
23
- title: many_comments
24
- author: eric
@@ -1,84 +0,0 @@
1
- require 'rubygems'
2
-
3
- require 'bundler'
4
- Bundler.setup
5
- # shoulda-matchers dependency:
6
- require 'active_support/core_ext/module/delegation'
7
- Bundler.require(:default, :development)
8
-
9
- if defined?(Debugger)
10
- ::Debugger.start
11
- ::Debugger.settings[:autoeval] = true if ::Debugger.respond_to?(:settings)
12
- end
13
-
14
- require 'test/unit'
15
- require 'mocha'
16
- require 'active_record'
17
- require "logger"
18
-
19
- raise "Must configure #time_zone_aware_attributes prior to models" if defined?(Post)
20
- ENV['TZ'] = 'utc'
21
- ActiveRecord::Base.time_zone_aware_attributes = true
22
- ActiveRecord::Base.logger = Logger.new(StringIO.new)
23
-
24
- require 'active_record/fixtures'
25
-
26
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
27
- $LOAD_PATH.unshift(File.dirname(__FILE__))
28
- require 'kasket'
29
-
30
- Kasket.setup
31
-
32
- class ActiveSupport::TestCase
33
- include ActiveRecord::TestFixtures
34
-
35
- def create_fixtures(*table_names)
36
- if block_given?
37
- Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names) { yield }
38
- else
39
- Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names)
40
- end
41
- end
42
-
43
- self.use_transactional_fixtures = true
44
-
45
- self.use_instantiated_fixtures = false
46
-
47
- setup :clear_cache
48
- def clear_cache
49
- Kasket.cache.clear
50
- end
51
-
52
- def arel?
53
- self.class.arel?
54
- end
55
-
56
- def self.arel?
57
- ActiveRecord::VERSION::MAJOR >= 3 && ActiveRecord::VERSION::MINOR >= 1
58
- end
59
- end
60
-
61
- ActiveSupport::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures/"
62
- $LOAD_PATH.unshift(ActiveSupport::TestCase.fixture_path)
63
-
64
- class ActiveSupport::TestCase
65
- fixtures :all
66
- end
67
-
68
- module Rails
69
- module_function
70
- CACHE = ActiveSupport::Cache::MemoryStore.new
71
- LOGGER = Logger.new(STDOUT)
72
-
73
- def cache
74
- CACHE
75
- end
76
-
77
- def logger
78
- LOGGER
79
- end
80
- end
81
-
82
- require 'test_models'
83
- POST_VERSION = Post.column_names.join.sum
84
- COMMENT_VERSION = Comment.column_names.join.sum
@@ -1,153 +0,0 @@
1
- require File.expand_path("helper", File.dirname(__FILE__))
2
- require 'kasket/query_parser'
3
-
4
- class ParserTest < ActiveSupport::TestCase
5
- def parse(options)
6
- scope = Post
7
- if arel?
8
- options.each do |k,v|
9
- scope = case k
10
- when :conditions then scope.where(v)
11
- else
12
- scope.send(k, v)
13
- end
14
- end
15
- scope.to_kasket_query
16
- elsif ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
17
- @parser.parse(scope.scoped(options).to_sql)
18
- else
19
- sql = scope.send(:construct_finder_sql, options)
20
- @parser.parse(sql)
21
- end
22
- end
23
-
24
- context "Parsing" do
25
- setup do
26
- @parser = Kasket::QueryParser.new(Post)
27
- end
28
-
29
- should "not support conditions with number as column (e.g. 0 = 1)" do
30
- assert !parse(:conditions => "0 = 1")
31
- end
32
-
33
- should "not support conditions with number as column and parans (e.g. 0 = 1)" do
34
- assert !parse(:conditions => "(0 = 1)")
35
- end
36
-
37
- should "not support :order" do
38
- assert !parse(:conditions => "id = 1", :order => "xxx")
39
- end
40
-
41
- should 'not support IN queries in combination with other conditions' do
42
- assert !parse(:conditions => {:id => [1,2,3], :is_active => true})
43
- end
44
-
45
- should "extract conditions" do
46
- kasket_query = parse(:conditions => {:title => 'red', :blog_id => 1})
47
- assert_equal [[:blog_id, "1"], [:title, "red"]], kasket_query[:attributes]
48
- end
49
-
50
- should "extract conditions with parans that do not surround" do
51
- kasket_query = parse(:conditions => "(title = 'red') AND (blog_id = 1)")
52
- if ActiveRecord::VERSION::STRING > "3.1.0"
53
- assert !kasket_query
54
- else
55
- assert_equal [[:blog_id, "1"], [:title, "red"]], kasket_query[:attributes]
56
- end
57
- end
58
-
59
- should "extract required index" do
60
- assert_equal [:blog_id, :title], parse(:conditions => {:title => 'red', :blog_id => 1})[:index]
61
- end
62
-
63
- should "only support queries against its model's table" do
64
- assert !parse(:conditions => {'blogs.id' => 2}, :from => 'apples')
65
- end
66
-
67
- should "support cachable queries" do
68
- assert parse(:conditions => {:id => 1})
69
- assert parse(:conditions => {:id => 1}, :limit => 1)
70
- end
71
-
72
- should "support IN queries on id" do
73
- assert_equal [[:id, ['1', '2', '3']]], parse(:conditions => {:id => [1,2,3]})[:attributes]
74
- end
75
-
76
- should "not support IN queries on other attributes" do
77
- assert !parse(:conditions => {:hest => [1,2,3]})
78
- end
79
-
80
- should "support vaguely formatted queries" do
81
- assert @parser.parse('SELECT * FROM "posts" WHERE (title = red AND blog_id = big)')
82
- end
83
-
84
- context "extract options" do
85
- should "provide the limit" do
86
- assert_equal nil, parse(:conditions => {:id => 2})[:limit]
87
- assert_equal 1, parse(:conditions => {:id => 2}, :limit => 1)[:limit]
88
- end
89
- end
90
-
91
- context "unsupported queries" do
92
- should "include advanced limits" do
93
- assert !parse(:conditions => {:title => 'red', :blog_id => 1}, :limit => 2)
94
- end
95
-
96
- should "include joins" do
97
- assert !parse(:conditions => {:title => 'test', 'apple.tree_id' => 'posts.id'}, :from => ['posts', 'apple'])
98
- assert !parse(:conditions => {:title => 'test'}, :joins => :comments)
99
- end
100
-
101
- should "include specific selects" do
102
- assert !parse(:conditions => {:title => 'red'}, :select => :id)
103
- end
104
-
105
- should "include offset" do
106
- assert !parse(:conditions => {:title => 'red'}, :limit => 1, :offset => 2)
107
- end
108
-
109
- should "include order" do
110
- assert !parse(:conditions => {:title => 'red'}, :order => :title)
111
- end
112
-
113
- should "include the OR operator" do
114
- assert !parse(:conditions => "title = 'red' OR blog_id = 1")
115
- end
116
- end
117
-
118
- context "key generation" do
119
- should "include the table name and version" do
120
- kasket_query = parse(:conditions => {:id => 1})
121
- assert_match(/^kasket-#{Kasket::Version::PROTOCOL}\/R#{ActiveRecord::VERSION::MAJOR}#{ActiveRecord::VERSION::MINOR}\/posts\/version=#{POST_VERSION}\//, kasket_query[:key])
122
- end
123
-
124
- should "include all indexed attributes" do
125
- kasket_query = parse(:conditions => {:id => 1})
126
- assert_match(/id=1$/, kasket_query[:key])
127
-
128
- kasket_query = parse(:conditions => {:id => 1, :blog_id => 2})
129
- assert_match(/blog_id=2\/id=1$/, kasket_query[:key])
130
-
131
- kasket_query = parse(:conditions => {:id => 1, :title => 'title'})
132
- assert_match(/id=1\/title='title'$/, kasket_query[:key])
133
- end
134
-
135
- should "generate multiple keys on IN queries" do
136
- keys = parse(:conditions => {:id => [1,2]})[:key]
137
- assert_instance_of(Array, keys)
138
- assert_match(/id=1$/, keys[0])
139
- assert_match(/id=2$/, keys[1])
140
- end
141
-
142
- context "when limit 1" do
143
- should "add /first to the key if the index does not include id" do
144
- assert_match(/title='a'\/first$/, parse(:conditions => {:title => 'a'}, :limit => 1)[:key])
145
- end
146
-
147
- should "not add /first to the key when the index includes id" do
148
- assert_match(/id=1$/, parse(:conditions => {:id => 1}, :limit => 1)[:key])
149
- end
150
- end
151
- end
152
- end
153
- end