couch_potato-rspec 4.0.1 → 4.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 39ff80322f1452ee6f5dcdaa405c53e99a8a2279956e3389f6dfbbe9cc48211a
4
- data.tar.gz: 90e9e8a14b5eec00401f572e0cb2a2be61010487237dbd04f554073a242f0745
3
+ metadata.gz: 9187c364f8b090f31efbda76468aca05fcf4e5082349419c360c6e497d0f945f
4
+ data.tar.gz: d34ff1403b51771aa0b2718cf2e4558d026397217a708ba740d279a7df9de290
5
5
  SHA512:
6
- metadata.gz: ee2c2029a2d4169ab168c92793dcd7311dbc0bc4a366f74f2637b6b339eb123634451c7368aa0323901b4f33050b73f89eb409d2d4dcd00ed0ec3c0c76231a07
7
- data.tar.gz: c0ee60cb38de44fc5e03f692274592fe260a2b498878d711222e003bad723723af264475b5f61a7363fffe25d0e95c3d42e63826fc0d856e8d06e4151a2f2278
6
+ metadata.gz: 2b5c131ae6fc00d081fc2ff4cefbe6200b9eb8f9a0fec9ffd82421b5bf11addfe5484ff263319d8a5290b9d7494c5ea110b401f4fd493e49c29c47ddcabd4947
7
+ data.tar.gz: 2d46845862c4a4334b0eb162e63dc1be41046647edd33ecb53efd95d799516bfea6b14bbe9d693b38288f8e95b46000777e55af37ff09d697159bca9d026ecd6
@@ -0,0 +1,54 @@
1
+ module CouchPotato
2
+ module RSpec
3
+ class ListAsProxy
4
+ def initialize(results_ruby)
5
+ @results_ruby = results_ruby
6
+ end
7
+
8
+ def as(expected_ruby)
9
+ ListAsMatcher.new(expected_ruby, @results_ruby)
10
+ end
11
+ end
12
+
13
+ class ListAsMatcher
14
+ include ::RSpec::Matchers::Composable
15
+
16
+ def initialize(expected_ruby, results_ruby)
17
+ @expected_ruby = expected_ruby
18
+ @results_ruby = results_ruby
19
+ end
20
+
21
+ def matches?(view_spec)
22
+ js = <<-JS
23
+ (function() {
24
+ var results = #{@results_ruby.to_json};
25
+ var listed = '';
26
+ var list = #{view_spec.list_function};
27
+
28
+ var getRow = function() {
29
+ return results.rows.shift();
30
+ };
31
+ var send = function(text) {
32
+ listed = listed + text;
33
+ };
34
+ list();
35
+ return JSON.stringify(JSON.parse(listed));
36
+ })()
37
+
38
+ JS
39
+ @actual_ruby = JSON.parse(ExecJS.eval(js))
40
+
41
+ values_match? @expected_ruby, @actual_ruby
42
+ end
43
+
44
+ def failure_message
45
+ "Expected to list as #{@expected_ruby.inspect} but got #{@actual_ruby.inspect}."
46
+ end
47
+
48
+ def failure_message_when_negated
49
+ "Expected to not list as #{@expected_ruby.inspect} but did."
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,204 @@
1
+ require 'json'
2
+
3
+ module CouchPotato
4
+ module RSpec
5
+ class MapReduceToProxy
6
+ def initialize(*input_ruby)
7
+ @input_ruby, @options = input_ruby.flatten, {}
8
+ end
9
+
10
+ def with_options(options)
11
+ @options = options
12
+ self
13
+ end
14
+
15
+ def to(*expected_ruby)
16
+ MapReduceToMatcher.new(expected_ruby, @input_ruby, @options)
17
+ end
18
+ end
19
+
20
+ class MapReduceToMatcher
21
+ include ::RSpec::Matchers::Composable
22
+
23
+ def initialize(expected_ruby, input_ruby, options)
24
+ @expected_ruby = expected_ruby
25
+ @input_ruby = input_ruby
26
+ @options = options
27
+ end
28
+
29
+ def matches?(view_spec)
30
+ js = <<-JS
31
+ (function() {
32
+ var sum = function(values) {
33
+ return values.reduce(function(memo, value) { return memo + value; });
34
+ };
35
+ // Equivalents of couchdb built-in reduce functions whose names can be
36
+ // given as the reduce function in the view_spec:
37
+ var _sum = function(keys, values, rereduce) {
38
+ return sum(values);
39
+ }
40
+ var _count = function(keys, values, rereduce) {
41
+ if (rereduce) {
42
+ return sum(values);
43
+ } else {
44
+ return values.length;
45
+ }
46
+ }
47
+ var _stats = function(keys, values, rereduce) {
48
+ var result = {sum: 0, count: 0, min: Number.MAX_VALUE, max: Number.MIN_VALUE, sumsqr: 0};
49
+ if (rereduce) {
50
+ for (var i in values) {
51
+ var value = values[i];
52
+ result.sum += value.sum;
53
+ result.count += value.count;
54
+ result.min = Math.min(result.min, value.min);
55
+ result.max = Math.max(result.max, value.max);
56
+ result.sumsqr += value.sumsqr;
57
+ }
58
+ } else {
59
+ for (var i in values) {
60
+ var value = values[i];
61
+ result.sum += value;
62
+ result.count += 1;
63
+ result.min = Math.min(result.min, value);
64
+ result.max = Math.max(result.max, value);
65
+ result.sumsqr += Math.pow(value, 2);
66
+ }
67
+ }
68
+ return result;
69
+ }
70
+
71
+ var docs = #{@input_ruby.to_json};
72
+ var options = #{@options.to_json};
73
+ var map = #{view_spec.map_function};
74
+ var reduce = #{view_spec.reduce_function};
75
+ var lib = #{view_spec.respond_to?(:lib) && view_spec.lib.to_json};
76
+ var collate = (function() { var module = {exports: {}}; var exports = module.exports; eval(#{File.read(File.expand_path(File.dirname(__FILE__) + '/../../../../vendor/pouchdb-collate/pouchdb-collate.js')).to_json}); return module.exports.collate;})();
77
+
78
+ // Map the input docs
79
+ var require = function(modulePath) {
80
+ var module = {exports: {}};
81
+ var exports = module.exports;
82
+ var pathArray = modulePath.split("/").slice(2);
83
+ var result = lib;
84
+ for (var i in pathArray) {
85
+ result = result[pathArray[i]];
86
+ }
87
+ eval(result);
88
+ return module.exports;
89
+ }
90
+
91
+ var unfilteredMapResults = [];
92
+ var emit = function(key, value) {
93
+ unfilteredMapResults.push({key: key, value: value});
94
+ };
95
+ for (var i in docs) {
96
+ map(docs[i]);
97
+ }
98
+
99
+ // Filter results by key/keys/startkey/endkey (if given).
100
+ var mapResults = [];
101
+ if (options.key) {
102
+ for (var r in unfilteredMapResults) {
103
+ var result = unfilteredMapResults[r];
104
+ if (collate(result.key, options.key) == 0)
105
+ mapResults.push(result);
106
+ }
107
+ }
108
+ // couchdb does not support multiple keys for reduce views without group=true
109
+ else if (options.keys && options.group) {
110
+ for (var r in unfilteredMapResults) {
111
+ var result = unfilteredMapResults[r];
112
+ for (var k in options.keys) {
113
+ var key = options.keys[k];
114
+ if (collate(result.key, key) == 0) {
115
+ mapResults.push(result);
116
+ break;
117
+ }
118
+ }
119
+ }
120
+ }
121
+ else if (options.startkey || options.endkey) {
122
+ for (var r in unfilteredMapResults) {
123
+ var result = unfilteredMapResults[r];
124
+ if (options.startkey && collate(options.startkey, result.key) > 0)
125
+ continue;
126
+ if (options.endkey && collate(result.key, options.endkey) > 0)
127
+ continue;
128
+ mapResults.push(result);
129
+ }
130
+ }
131
+ else {
132
+ mapResults = unfilteredMapResults;
133
+ }
134
+
135
+ // Group the map results, honoring the group and group_level options
136
+ var grouped = [];
137
+ if (options.group || options.group_level) {
138
+ var groupLevel = options.group_level;
139
+ if (groupLevel == "exact" || options.group == true)
140
+ groupLevel = 9999;
141
+
142
+ for (var mr in mapResults) {
143
+ var mapResult = mapResults[mr];
144
+ var groupedKey = Array.isArray(mapResult.key) ? mapResult.key.slice(0, groupLevel) : mapResult.key;
145
+ var groupFound = false;
146
+ for (var g in grouped) {
147
+ var group = grouped[g];
148
+ if (collate(groupedKey, group.groupedKey) == 0) {
149
+ group.keys.push(mapResult.key);
150
+ group.values.push(mapResult.value);
151
+ groupFound = true;
152
+ break;
153
+ }
154
+ }
155
+
156
+ if (!groupFound)
157
+ grouped.push({keys: [mapResult.key], groupedKey: groupedKey, values: [mapResult.value]});
158
+ }
159
+ } else {
160
+ var group = {keys: null, groupedKey: null, values: []};
161
+ for (var mr in mapResults)
162
+ group.values.push(mapResults[mr].value);
163
+ grouped.push(group);
164
+ }
165
+
166
+ // Reduce the grouped map results
167
+ var results = [];
168
+ for (var g in grouped) {
169
+ var group = grouped[g], reduced = null;
170
+ if (group.values.length >= 2) {
171
+ // Split the values into two parts, reduce each part, then rereduce those results
172
+ var mid = parseInt(group.values.length / 2);
173
+ var keys1 = (group.keys || []).slice(0, mid),
174
+ values1 = group.values.slice(0, mid);
175
+ var reduced1 = reduce(keys1, values1, false);
176
+ var keys2 = (group.keys || []).slice(mid, group.values.length),
177
+ values2 = group.values.slice(mid, group.values.length);
178
+ var reduced2 = reduce(keys2, values2, false);
179
+ reduced = reduce(null, [reduced1, reduced2], true);
180
+ } else {
181
+ // Not enough values to split, so just reduce, and then rereduce the single result
182
+ reduced = reduce(group.keys, group.values, false);
183
+ reduced = reduce(null, [reduced], true);
184
+ }
185
+ results.push({key: group.groupedKey, value: reduced});
186
+ }
187
+
188
+ return JSON.stringify(results);
189
+ })()
190
+ JS
191
+ @actual_ruby = JSON.parse(ExecJS.eval(js))
192
+ values_match? @expected_ruby, @actual_ruby
193
+ end
194
+
195
+ def failure_message
196
+ "Expected to map/reduce to #{@expected_ruby.inspect} but got #{@actual_ruby.inspect}."
197
+ end
198
+
199
+ def failure_message_when_negated
200
+ "Expected not to map/reduce to #{@actual_ruby.inspect} but did."
201
+ end
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,63 @@
1
+ require 'json'
2
+
3
+
4
+ module CouchPotato
5
+ module RSpec
6
+ class MapToProxy
7
+ def initialize(input_ruby)
8
+ @input_ruby = input_ruby
9
+ end
10
+
11
+ def to(*expected_ruby)
12
+ MapToMatcher.new(expected_ruby, @input_ruby)
13
+ end
14
+ end
15
+
16
+ class MapToMatcher
17
+ include ::RSpec::Matchers::Composable
18
+
19
+ def initialize(expected_ruby, input_ruby)
20
+ @expected_ruby = expected_ruby
21
+ @input_ruby = input_ruby
22
+ end
23
+
24
+ def matches?(view_spec)
25
+ js = <<-JS
26
+ (function() {
27
+ var doc = #{@input_ruby.to_json};
28
+ var map = #{view_spec.map_function};
29
+ var lib = #{view_spec.respond_to?(:lib) && view_spec.lib.to_json};
30
+ var result = [];
31
+ var require = function(modulePath) {
32
+ var module = {exports: {}};
33
+ var exports = module.exports;
34
+ var pathArray = modulePath.split("/").slice(2);
35
+ var result = lib;
36
+ for (var i in pathArray) {
37
+ result = result[pathArray[i]];
38
+ }
39
+ eval(result);
40
+ return module.exports;
41
+ }
42
+
43
+ var emit = function(key, value) {
44
+ result.push([key, value]);
45
+ };
46
+ map(doc);
47
+ return JSON.stringify(result);
48
+ })()
49
+ JS
50
+ @actual_ruby = JSON.parse(ExecJS.eval(js))
51
+ values_match? @expected_ruby, @actual_ruby
52
+ end
53
+
54
+ def failure_message
55
+ "Expected to map to #{@expected_ruby.inspect} but got #{@actual_ruby.inspect}."
56
+ end
57
+
58
+ def failure_message_when_negated
59
+ "Expected not to map to #{@actual_ruby.inspect} but did."
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,50 @@
1
+ module CouchPotato
2
+ module RSpec
3
+ class ReduceToProxy
4
+ def initialize(keys, values, rereduce = false)
5
+ @keys, @values, @rereduce = keys, values, rereduce
6
+ end
7
+
8
+ def to(expected_ruby)
9
+ ReduceToMatcher.new(expected_ruby, @keys, @values, @rereduce)
10
+ end
11
+ end
12
+
13
+ class ReduceToMatcher
14
+ include ::RSpec::Matchers::Composable
15
+
16
+ def initialize(expected_ruby, keys, values, rereduce = false)
17
+ @expected_ruby, @keys, @values, @rereduce = expected_ruby, keys, values, rereduce
18
+ end
19
+
20
+ def matches?(view_spec)
21
+ js = <<-JS
22
+ (function() {
23
+ sum = function(values) {
24
+ var rv = 0;
25
+ for (var i in values) {
26
+ rv += values[i];
27
+ }
28
+ return rv;
29
+ };
30
+
31
+ var keys = #{@keys.to_json};
32
+ var values = #{@values.to_json};
33
+ var reduce = #{view_spec.reduce_function};
34
+ return JSON.stringify({result: reduce(keys, values, #{@rereduce})});
35
+ })()
36
+ JS
37
+ @actual_ruby = JSON.parse(ExecJS.eval(js))['result']
38
+ values_match? @expected_ruby, @actual_ruby
39
+ end
40
+
41
+ def failure_message
42
+ "Expected to reduce to #{@expected_ruby.inspect} but got #{@actual_ruby.inspect}."
43
+ end
44
+
45
+ def failure_message_when_negated
46
+ "Expected not to reduce to #{@actual_ruby.inspect} but did."
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,30 @@
1
+ require 'execjs'
2
+
3
+ require 'couch_potato/rspec/matchers/map_to_matcher'
4
+ require 'couch_potato/rspec/matchers/reduce_to_matcher'
5
+ require 'couch_potato/rspec/matchers/map_reduce_to_matcher'
6
+ require 'couch_potato/rspec/matchers/list_as_matcher'
7
+
8
+ module RSpec
9
+ module Matchers
10
+ def map(document)
11
+ CouchPotato::RSpec::MapToProxy.new(document)
12
+ end
13
+
14
+ def reduce(keys, values)
15
+ CouchPotato::RSpec::ReduceToProxy.new(keys, values)
16
+ end
17
+
18
+ def rereduce(keys, values)
19
+ CouchPotato::RSpec::ReduceToProxy.new(keys, values, true)
20
+ end
21
+
22
+ def list(results)
23
+ CouchPotato::RSpec::ListAsProxy.new(results)
24
+ end
25
+
26
+ def map_reduce(*docs)
27
+ CouchPotato::RSpec::MapReduceToProxy.new(docs)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/mocks'
4
+ require 'active_support/core_ext/array'
5
+
6
+ module CouchPotato::RSpec
7
+ module StubView
8
+ class ViewStub
9
+ include RSpec::Mocks::ExampleMethods
10
+
11
+ def initialize(clazz, view, db)
12
+ @clazz = clazz
13
+ @view = view
14
+ @db = db
15
+ end
16
+
17
+ def with(*args, &block)
18
+ @args = args
19
+ and_return(block.call) if block
20
+ self
21
+ end
22
+
23
+ def and_return(return_value)
24
+ view_stub = double("#{@clazz}.#{@view}(#{@args.try(:join, ', ')}) view")
25
+ stub = allow(@clazz).to receive(@view)
26
+ stub.with(*@args) if @args
27
+ stub.and_return(view_stub)
28
+ allow(@db).to receive(:view).with(view_stub).and_return(return_value)
29
+ return unless return_value.respond_to?(:first)
30
+
31
+ allow(@db).to receive(:first).with(view_stub).and_return(return_value.first)
32
+ allow(@db)
33
+ .to receive(:view_in_batches) do |_view, batch_size: CouchPotato::Database.default_batch_size, &block|
34
+ batches = return_value.in_groups_of(batch_size, false)
35
+ batches.each(&block)
36
+ end
37
+ .with(view_stub, any_args)
38
+
39
+ if return_value.first
40
+ allow(@db).to receive(:first!).with(view_stub).and_return(return_value.first)
41
+ else
42
+ allow(@db).to receive(:first!).with(view_stub).and_raise(CouchPotato::NotFound)
43
+ end
44
+ end
45
+ end
46
+
47
+ def stub_view(clazz, view, &block)
48
+ stub = ViewStub.new clazz, view, self
49
+ stub.and_return(block.call) if block
50
+ stub
51
+ end
52
+ end
53
+
54
+ module StubDb
55
+ include ::RSpec::Mocks::ExampleMethods
56
+
57
+ def stub_db(options = {})
58
+ db = double(:db, options)
59
+ db.extend CouchPotato::RSpec::StubView
60
+ allow(self).to receive(:database) { db }
61
+ db
62
+ end
63
+ end
64
+
65
+ ::CouchPotato.extend StubDb
66
+ end
@@ -0,0 +1,2 @@
1
+ require 'couch_potato/rspec/stub_db'
2
+ require 'couch_potato/rspec/matchers'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: couch_potato-rspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.1
4
+ version: 4.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Lang
@@ -58,6 +58,13 @@ executables: []
58
58
  extensions: []
59
59
  extra_rdoc_files: []
60
60
  files:
61
+ - lib/couch_potato/rspec.rb
62
+ - lib/couch_potato/rspec/matchers.rb
63
+ - lib/couch_potato/rspec/matchers/list_as_matcher.rb
64
+ - lib/couch_potato/rspec/matchers/map_reduce_to_matcher.rb
65
+ - lib/couch_potato/rspec/matchers/map_to_matcher.rb
66
+ - lib/couch_potato/rspec/matchers/reduce_to_matcher.rb
67
+ - lib/couch_potato/rspec/stub_db.rb
61
68
  - spec/unit/rspec_matchers_spec.rb
62
69
  homepage: http://github.com/langalex/couch_potato
63
70
  licenses: []