analysand 1.0.1

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.
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+
3
+ require 'analysand/errors'
4
+ require 'analysand/instance'
5
+ require 'uri'
6
+ require 'vcr'
7
+
8
+ require File.expand_path('../a_session_grantor', __FILE__)
9
+
10
+ module Analysand
11
+ describe Instance do
12
+ describe '#initialize' do
13
+ it 'requires an absolute URI' do
14
+ lambda { Instance.new(URI("/abc")) }.should raise_error(InvalidURIError)
15
+ end
16
+ end
17
+
18
+ let(:instance) { Instance.new(instance_uri) }
19
+
20
+ describe '#establish_session' do
21
+ describe 'given admin credentials' do
22
+ let(:credentials) { admin_credentials }
23
+
24
+ it_should_behave_like 'a session grantor'
25
+ end
26
+
27
+ describe 'given member credentials' do
28
+ let(:credentials) { member1_credentials }
29
+
30
+ it_should_behave_like 'a session grantor'
31
+ end
32
+
33
+ describe 'given incorrect credentials' do
34
+ it 'returns [nil, response]' do
35
+ session, resp = instance.establish_session('wrong', 'wrong')
36
+
37
+ session.should be_nil
38
+ resp.code.should == '401'
39
+ end
40
+ end
41
+ end
42
+
43
+ describe '#renew_session' do
44
+ let(:credentials) { admin_credentials }
45
+
46
+ before do
47
+ @session, _ = instance.establish_session(credentials[:username], credentials[:password])
48
+ end
49
+
50
+ describe 'if CouchDB refreshes the session cookie' do
51
+ around do |example|
52
+ VCR.use_cassette('get_session_refreshes_cookie') { example.call }
53
+ end
54
+
55
+ it_should_behave_like 'a session grantor' do
56
+ let(:result) { instance.renew_session(@session) }
57
+ let(:role_locator) do
58
+ lambda { |resp| resp['userCtx']['roles'] }
59
+ end
60
+ end
61
+ end
62
+
63
+ describe 'if CouchDB does not refresh the session cookie' do
64
+ around do |example|
65
+ VCR.use_cassette('get_session_does_not_refresh_cookie') { example.call }
66
+ end
67
+
68
+ it_should_behave_like 'a session grantor' do
69
+ let(:result) { instance.renew_session(@session) }
70
+ let(:role_locator) do
71
+ lambda { |resp| resp['userCtx']['roles'] }
72
+ end
73
+ end
74
+ end
75
+
76
+ describe 'given an invalid session' do
77
+ it 'returns [nil, response]' do
78
+ session, resp = instance.renew_session({ :token => 'AuthSession=wrong' })
79
+
80
+ session.should be_nil
81
+ resp.code.should == '400'
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ require 'analysand/database'
4
+ require 'analysand/response'
5
+
6
+ module Analysand
7
+ describe Response do
8
+ let(:db) { Database.new(database_uri) }
9
+
10
+ describe '#etag' do
11
+ let(:response) do
12
+ VCR.use_cassette('head_request_with_etag') do
13
+ db.head('abc123', admin_credentials)
14
+ end
15
+ end
16
+
17
+ it 'removes quotes from ETags' do
18
+ response.etag.should == '1-967a00dff5e02add41819138abb3284d'
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ require 'analysand/view_response'
4
+
5
+ module Analysand
6
+ describe ViewResponse do
7
+ describe '#docs' do
8
+ let(:resp_with_docs) do
9
+ '{"rows": [{"id":"foo","key":"foo","value":{},"doc":{"foo":"bar"}}]}'
10
+ end
11
+
12
+ let(:resp_without_docs) do
13
+ '{"rows": [{"id":"foo","key":"foo","value":{}}]}'
14
+ end
15
+
16
+ describe 'if the view includes docs' do
17
+ subject { ViewResponse.new(stub(:body => resp_with_docs)) }
18
+
19
+ it 'returns the value of the "doc" key in each row' do
20
+ subject.docs.should == [{'foo' => 'bar'}]
21
+ end
22
+ end
23
+
24
+ describe 'if the view does not include docs' do
25
+ subject { ViewResponse.new(stub(:body => resp_without_docs)) }
26
+
27
+ it 'returns []' do
28
+ subject.docs.should == []
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,73 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: http://localhost:5984/_session
6
+ body:
7
+ string: name=admin&password=admin
8
+ headers:
9
+ Connection:
10
+ - keep-alive
11
+ Accept:
12
+ - "*/*"
13
+ Keep-Alive:
14
+ - 30
15
+ Content-Type:
16
+ - application/x-www-form-urlencoded
17
+ response:
18
+ status:
19
+ code: 200
20
+ message: OK
21
+ headers:
22
+ Date:
23
+ - Sat, 31 Mar 2012 22:35:18 GMT
24
+ Set-Cookie:
25
+ - AuthSession=YWRtaW46NEY3Nzg2QTY6WVqDNxuHpu4OPp07rib-n9WQLt8; Version=1; Path=/; HttpOnly
26
+ Server:
27
+ - CouchDB/1.3.0a-d0a5bf0-git (Erlang OTP/R14B02)
28
+ Content-Length:
29
+ - "56"
30
+ Content-Type:
31
+ - text/plain; charset=utf-8
32
+ Cache-Control:
33
+ - must-revalidate
34
+ body:
35
+ string: |
36
+ {"ok":true,"name":null,"roles":["_admin","site_admin"]}
37
+ recorded_at: Sat, 31 Mar 2012 22:35:18 GMT
38
+ - request:
39
+ method: get
40
+ uri: http://localhost:5984/_session
41
+ headers:
42
+ Cookie:
43
+ - AuthSession=YWRtaW46NEY3Nzg2QTY6WVqDNxuHpu4OPp07rib-n9WQLt8; Version=1; Path=/; HttpOnly
44
+ Connection:
45
+ - keep-alive
46
+ Accept:
47
+ - "*/*"
48
+ Keep-Alive:
49
+ - 30
50
+ Content-Type:
51
+ - application/x-www-form-urlencoded
52
+ response:
53
+ status:
54
+ code: 200
55
+ message: OK
56
+ headers:
57
+ Date:
58
+ - Sat, 31 Mar 2012 22:35:18 GMT
59
+ Server:
60
+ - CouchDB/1.3.0a-d0a5bf0-git (Erlang OTP/R14B02)
61
+ Content-Length:
62
+ - "56"
63
+ Content-Type:
64
+ - text/plain; charset=utf-8
65
+ Cache-Control:
66
+ - must-revalidate
67
+ body:
68
+ string: |
69
+ {"ok":true,"userCtx":{"name":null,"roles":["_admin","site_admin"]},"info":{"authentication_db":"_users","authentication_handlers":["oauth","cookie","default"]}}
70
+
71
+ http_version:
72
+ recorded_at: Sat, 31 Mar 2012 22:35:18 GMT
73
+ recorded_with: VCR 2.0.1
@@ -0,0 +1,75 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: http://localhost:5984/_session
6
+ body:
7
+ string: name=admin&password=admin
8
+ headers:
9
+ Connection:
10
+ - keep-alive
11
+ Accept:
12
+ - "*/*"
13
+ Keep-Alive:
14
+ - 30
15
+ Content-Type:
16
+ - application/x-www-form-urlencoded
17
+ response:
18
+ status:
19
+ code: 200
20
+ message: OK
21
+ headers:
22
+ Date:
23
+ - Sat, 31 Mar 2012 22:35:18 GMT
24
+ Set-Cookie:
25
+ - AuthSession=YWRtaW46NEY3Nzg2QTY6WVqDNxuHpu4OPp07rib-n9WQLt8; Version=1; Path=/; HttpOnly
26
+ Server:
27
+ - CouchDB/1.3.0a-d0a5bf0-git (Erlang OTP/R14B02)
28
+ Content-Length:
29
+ - "56"
30
+ Content-Type:
31
+ - text/plain; charset=utf-8
32
+ Cache-Control:
33
+ - must-revalidate
34
+ body:
35
+ string: |
36
+ {"ok":true,"name":null,"roles":["_admin","site_admin"]}
37
+ recorded_at: Sat, 31 Mar 2012 22:35:18 GMT
38
+ - request:
39
+ method: get
40
+ uri: http://localhost:5984/_session
41
+ headers:
42
+ Cookie:
43
+ - AuthSession=YWRtaW46NEY3Nzg2QTY6WVqDNxuHpu4OPp07rib-n9WQLt8; Version=1; Path=/; HttpOnly
44
+ Connection:
45
+ - keep-alive
46
+ Accept:
47
+ - "*/*"
48
+ Keep-Alive:
49
+ - 30
50
+ Content-Type:
51
+ - application/x-www-form-urlencoded
52
+ response:
53
+ status:
54
+ code: 200
55
+ message: OK
56
+ headers:
57
+ Date:
58
+ - Sat, 31 Mar 2012 22:35:18 GMT
59
+ Set-Cookie:
60
+ - AuthSession=YWRtaW46NEY3Nzg3RTk6Lj1LSc4dWHBMZoFKm9ZMxq0L3Ec; Version=1; Path=/; HttpOnly
61
+ Server:
62
+ - CouchDB/1.3.0a-d0a5bf0-git (Erlang OTP/R14B02)
63
+ Content-Length:
64
+ - "56"
65
+ Content-Type:
66
+ - text/plain; charset=utf-8
67
+ Cache-Control:
68
+ - must-revalidate
69
+ body:
70
+ string: |
71
+ {"ok":true,"userCtx":{"name":null,"roles":["_admin","site_admin"]},"info":{"authentication_db":"_users","authentication_handlers":["oauth","cookie","default"]}}
72
+
73
+ http_version:
74
+ recorded_at: Sat, 31 Mar 2012 22:35:18 GMT
75
+ recorded_with: VCR 2.0.1
@@ -0,0 +1,40 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: head
5
+ uri: http://admin:admin@localhost:5984/analysand_test/abc123
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept:
11
+ - ! '*/*'
12
+ User-Agent:
13
+ - Ruby
14
+ Connection:
15
+ - keep-alive
16
+ Keep-Alive:
17
+ - 30
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Server:
24
+ - CouchDB/1.3.0a-d6ab08d-git (Erlang OTP/R15B)
25
+ Etag:
26
+ - ! '"1-967a00dff5e02add41819138abb3284d"'
27
+ Date:
28
+ - Tue, 22 May 2012 14:21:26 GMT
29
+ Content-Type:
30
+ - text/plain; charset=utf-8
31
+ Content-Length:
32
+ - '61'
33
+ Cache-Control:
34
+ - must-revalidate
35
+ body:
36
+ encoding: US-ASCII
37
+ string: ''
38
+ http_version:
39
+ recorded_at: Tue, 22 May 2012 14:21:26 GMT
40
+ recorded_with: VCR 2.0.1
@@ -0,0 +1,19 @@
1
+ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
2
+
3
+ require File.expand_path('../support/database_access', __FILE__)
4
+ require File.expand_path('../support/example_isolation', __FILE__)
5
+ require File.expand_path('../support/test_parameters', __FILE__)
6
+
7
+ require 'vcr'
8
+
9
+ RSpec.configure do |config|
10
+ config.include DatabaseAccess
11
+ config.include ExampleIsolation
12
+ config.include TestParameters
13
+ end
14
+
15
+ VCR.configure do |c|
16
+ c.allow_http_connections_when_no_cassette = true
17
+ c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
18
+ c.hook_into :webmock
19
+ end
@@ -0,0 +1,40 @@
1
+ require 'net/http'
2
+
3
+ module DatabaseAccess
4
+ ##
5
+ # Sets members and admins for the database.
6
+ #
7
+ # Assumes #admin_credentials returns a Hash containing the keys :username and
8
+ # :password, where those keys' values are the username and password of a
9
+ # CouchDB admin user.
10
+ def set_security(members, admins = {})
11
+ doc = {
12
+ 'members' => members,
13
+ 'admins' => admins
14
+ }
15
+
16
+ credentials = admin_credentials
17
+ uri = instance_uri
18
+
19
+ Net::HTTP.start(uri.host, uri.port) do |h|
20
+ req = Net::HTTP::Put.new("/#{database_name}/_security")
21
+ req.basic_auth(credentials[:username], credentials[:password])
22
+ req.body = doc.to_json
23
+
24
+ resp = h.request(req)
25
+
26
+ unless Net::HTTPSuccess === resp
27
+ raise "Unable to set security parameters on #{database_name}: response code = #{resp.code}, body = #{resp.body}"
28
+ end
29
+ end
30
+ end
31
+
32
+ ##
33
+ # Resets member and admin lists for the test database to [].
34
+ def clear_security
35
+ set_security({ 'users' => [], 'roles' => [] },
36
+ { 'users' => [], 'roles' => [] })
37
+ end
38
+ end
39
+
40
+ # vim:ts=2:sw=2:et:tw=78
@@ -0,0 +1,86 @@
1
+ require File.expand_path('../test_parameters', __FILE__)
2
+
3
+ ##
4
+ # CouchDB doesn't implement a commit/rollback scheme like other databases, but
5
+ # it would nevertheless still sometimes be nice to be able to isolate
6
+ # datastore changes made between tests.
7
+ #
8
+ # This module provides methods to drop and create databases, which provides a
9
+ # slow-yet-effective way of achieving said isolation. Examples:
10
+ #
11
+ # describe Something do
12
+ # let(:database_uri) { 'http://localhost:5984' }
13
+ # let(:database_name) { 'something' }
14
+ #
15
+ # before do
16
+ # clean_databases!
17
+ # end
18
+ # end
19
+ #
20
+ # describe AnotherThing do
21
+ # let(:database_uri) { 'http://localhost:5984' }
22
+ # let(:database_names) { ['another', 'database'] }
23
+ #
24
+ # before do
25
+ # clean_databases!
26
+ # end
27
+ # end
28
+ #
29
+ # If both database_name and database_names are specified in the same example
30
+ # group, the latter will have precedence.
31
+ #
32
+ #
33
+ # Clean databases come with a performance cost
34
+ # ============================================
35
+ #
36
+ # Dropping and creating databases has a real performance cost, so it is
37
+ # advised that you only use this when absolutely necessary. Databases are
38
+ # only cleaned when
39
+ #
40
+ # clean_databases!
41
+ #
42
+ # is included in an example group's before block.
43
+ module ExampleIsolation
44
+ def clean_databases!
45
+ drop_databases!
46
+ create_databases!
47
+ end
48
+
49
+ def drop_databases!
50
+ affected_databases.each do |db|
51
+ uri = instance_uri + "/#{db}"
52
+ credentials = admin_credentials
53
+
54
+ Net::HTTP.start(uri.host, uri.port) do |http|
55
+ req = Net::HTTP::Delete.new(uri.path)
56
+ req.basic_auth(admin_username, admin_password)
57
+
58
+ http.request(req)
59
+ end
60
+ end
61
+ end
62
+
63
+ def create_databases!
64
+ affected_databases.each do |db|
65
+ uri = instance_uri + "/#{db}"
66
+ credentials = admin_credentials
67
+
68
+ Net::HTTP.start(uri.host, uri.port) do |http|
69
+ req = Net::HTTP::Put.new(uri.path)
70
+ req.basic_auth(admin_username, admin_password)
71
+
72
+ http.request(req)
73
+ end
74
+ end
75
+ end
76
+
77
+ def affected_databases
78
+ if respond_to?(:database_name)
79
+ [database_name]
80
+ elsif respond_to?(:database_names)
81
+ database_names
82
+ end.flatten
83
+ end
84
+ end
85
+
86
+ # vim:ts=2:sw=2:et:tw=78