couch_potato-rspec 4.0.1 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []