cloud_encrypted_sync 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -9,7 +9,7 @@ GEM
9
9
  activesupport (3.2.3)
10
10
  i18n (~> 0.6)
11
11
  multi_json (~> 1.0)
12
- fakefs (0.4.0)
12
+ fakefs (0.4.1)
13
13
  i18n (0.6.0)
14
14
  metaclass (0.0.1)
15
15
  mocha (0.11.4)
data/bin/ces CHANGED
@@ -2,4 +2,4 @@
2
2
  require 'optparse'
3
3
  require 'etc'
4
4
  require File.expand_path('../../lib/cloud_encrypted_sync', __FILE__)
5
- CloudEncryptedSync::Master.activate!
5
+ CloudEncryptedSync::Synchronizer.run
@@ -38,14 +38,11 @@ module CloudEncryptedSync
38
38
 
39
39
  def register_subclass_with_parent(subclass)
40
40
  name = formated_name_of(subclass)
41
- if children[name]
42
- raise Errors::RegistrationError.new("#{name} already registered")
43
- else
44
- children[name] = subclass
45
- end
41
+ children[name] ||= subclass
46
42
  end
47
43
 
48
44
  def formated_name_of(subclass)
45
+ puts "Subclass: #{subclass}"
49
46
  subclass.name.match(/([^:]+)$/)[0].underscore.to_sym
50
47
  end
51
48
  end
@@ -57,7 +57,7 @@ module CloudEncryptedSync
57
57
 
58
58
  @option_parser = OptionParser.new do |opts|
59
59
  opts.banner = "Usage: #{executable_name} [options] /path/to/folder/to/sync"
60
- opts.on('--data-dir PATH',"Data directory where snapshots and config file are found.") do |path|
60
+ opts.on('--data-dir PATH',"Data directory where indexes and config file are found.") do |path|
61
61
  clo[:data_dir] = path
62
62
  end
63
63
  opts.on('--adapter ADAPTERNAME', 'Name of cloud adapter to use.') do |adapter_name|
@@ -4,7 +4,6 @@ module CloudEncryptedSync
4
4
 
5
5
  class << self
6
6
 
7
-
8
7
  def encrypt_data(data)
9
8
  iv = generate_random_iv
10
9
  encrypted_data = crypt_data(:encrypt, iv, data)
@@ -21,10 +20,6 @@ module CloudEncryptedSync
21
20
  Digest::SHA2.hexdigest(data,512)
22
21
  end
23
22
 
24
- def generate_random_key
25
- initialized_cipher.random_key.unpack('H*')[0]
26
- end
27
-
28
23
  #######
29
24
  private
30
25
  #######
@@ -5,12 +5,12 @@ module CloudEncryptedSync
5
5
  class << self
6
6
 
7
7
  def write(data,key)
8
- stored_data[key] = data
8
+ stored_data[bucket_name][key] = data
9
9
  end
10
10
 
11
11
  def parse_command_line_options(opts,command_line_options)
12
12
  opts.on('--bucket BUCKETNAME', 'Name of cloud adapter to use.') do |bucket_name|
13
- command_line_options[:bucket_name] = bucket_name
13
+ command_line_options[:bucket] = bucket_name
14
14
  end
15
15
  return command_line_options
16
16
  end
@@ -37,7 +37,7 @@ module CloudEncryptedSync
37
37
  end
38
38
 
39
39
  def bucket_name
40
- Configuration.settings[:bucket_name].to_sym
40
+ Configuration.settings[:bucket].to_sym
41
41
  end
42
42
 
43
43
  end
@@ -1,6 +1,5 @@
1
1
  module CloudEncryptedSync
2
2
  module Errors
3
- class RegistrationError < RuntimeError; end
4
3
  class IncompleteConfigurationError < RuntimeError; end
5
4
  class NoSuchKey < RuntimeError; end
6
5
  class TemplateMethodCalled < RuntimeError; end
@@ -18,15 +18,15 @@ module CloudEncryptedSync
18
18
  def write
19
19
  local_hash = compile_local_hash #recompile
20
20
  liaison.push(local_hash.to_yaml,index_key) #push to remote
21
- File.open(snapshot_path, 'w') { |file| YAML.dump(local_hash, file) } #save to local
21
+ File.open(index_path, 'w') { |file| YAML.dump(local_hash, file) } #save to local
22
22
  end
23
23
 
24
24
  def full_file_path(relative_path)
25
- normalized_sync_path+'/'+relative_path
25
+ normalized_sync_path+relative_path
26
26
  end
27
27
 
28
- def snapshot_path
29
- "#{Configuration.data_folder_path}/#{snapshot_filename}"
28
+ def index_path
29
+ "#{Configuration.data_folder_path}/#{index_filename}"
30
30
  end
31
31
 
32
32
  def file_key(full_path)
@@ -60,7 +60,7 @@ module CloudEncryptedSync
60
60
  @index_key ||= Cryptographer.hash_data(Configuration.settings[:encryption_key])
61
61
  end
62
62
 
63
- def snapshot_filename
63
+ def index_filename
64
64
  "#{normalized_sync_path.gsub(/[^A-Za-z0-9]/,'_')}.index.yml"
65
65
  end
66
66
 
@@ -1,27 +1,23 @@
1
1
  module CloudEncryptedSync
2
- class Master
2
+ class Synchronizer
3
3
 
4
4
  class << self
5
5
  attr_accessor :finalize_required
6
6
 
7
- def activate!
8
- sync
9
- end
10
-
11
- def sync
7
+ def run
12
8
  begin
13
- CloudEncryptedSync::Master.delete_local_files!
14
- CloudEncryptedSync::Master.delete_remote_files!
15
- CloudEncryptedSync::Master.pull_files!
16
- CloudEncryptedSync::Master.push_files!
17
- CloudEncryptedSync::Master.finalize!
9
+ delete_local_files
10
+ delete_remote_files
11
+ pull_files
12
+ push_files
13
+ finalize
18
14
  rescue Errors::IncompleteConfigurationError => exception
19
15
  puts exception.message
20
16
  end
21
17
  end
22
18
 
23
- def push_files!
24
- progress_meter = ProgressMeter.new(files_to_pull.keys.size,:label => 'Pushing Files: ')
19
+ def push_files
20
+ progress_meter = ProgressMeter.new(files_to_push.keys.size,:label => 'Pushing Files: ')
25
21
  pushed_files_counter = 0
26
22
  files_to_push.each_pair do |key,relative_path|
27
23
  puts #newline for progress meter
@@ -38,7 +34,7 @@ module CloudEncryptedSync
38
34
  end
39
35
  end
40
36
 
41
- def pull_files!
37
+ def pull_files
42
38
  progress_meter = ProgressMeter.new(files_to_pull.keys.size,:label => 'Pulling Files: ')
43
39
  pulled_files_counter = 0
44
40
  files_to_pull.each_pair do |key,relative_path|
@@ -46,7 +42,7 @@ module CloudEncryptedSync
46
42
  puts #newline for progress meter
47
43
  if File.exist?(full_path) and (Index.file_key(full_path) == key)
48
44
  #already exists. probably left over from an earlier aborted pull
49
- puts "Not Pulling (already exists): #{path}"
45
+ puts "Not Pulling (already exists): #{full_path}"
50
46
  else
51
47
  Dir.mkdir(File.dirname(full_path)) unless File.exist?(File.dirname(full_path))
52
48
  puts "Pulling: #{relative_path}"
@@ -62,7 +58,7 @@ module CloudEncryptedSync
62
58
  end
63
59
  end
64
60
 
65
- def delete_remote_files!
61
+ def delete_remote_files
66
62
  remote_files_to_delete.each_pair do |key,path|
67
63
  puts "Deleting Remote: #{path}"
68
64
  liaison.delete(key)
@@ -70,10 +66,10 @@ module CloudEncryptedSync
70
66
  end
71
67
  end
72
68
 
73
- def delete_local_files!
69
+ def delete_local_files
74
70
  local_files_to_delete.each_pair do |key,relative_path|
75
71
  full_path = Index.full_file_path(relative_path)
76
- if !File.exist?(full_path) or (Index.file_key(full_path) == key)
72
+ if !File.exist?(full_path)
77
73
  puts "Not Deleting Local: #{relative_path}"
78
74
  else
79
75
  puts "Deleting Local: #{relative_path}"
@@ -83,7 +79,7 @@ module CloudEncryptedSync
83
79
  end
84
80
  end
85
81
 
86
- def finalize!
82
+ def finalize
87
83
  Index.write if finalize_required
88
84
  end
89
85
 
@@ -95,12 +91,8 @@ module CloudEncryptedSync
95
91
  AdapterLiaison.instance
96
92
  end
97
93
 
98
- def last_sync_date
99
- @last_sync_date ||= File.exist?(Index.snapshot_path) ? File.stat(Index.snapshot_path).ctime : nil
100
- end
101
-
102
94
  def last_sync_hash
103
- @last_sync_hash ||= File.exist?(Index.snapshot_path) ? YAML.load(File.read(Index.snapshot_path)) : {}
95
+ @last_sync_hash ||= File.exist?(Index.index_path) ? YAML.load(File.read(Index.index_path)) : {}
104
96
  end
105
97
 
106
98
  def files_to_push
@@ -1,3 +1,3 @@
1
1
  module CloudEncryptedSync
2
- VERSION = '0.1.1'
2
+ VERSION = '0.1.2'
3
3
  end
@@ -1,11 +1,10 @@
1
- require 'singleton'
2
- require 'yaml'
3
- require 'openssl'
1
+ require 'active_support/core_ext/string'
4
2
  require 'digest'
3
+ require 'fileutils'
5
4
  require 'find'
6
- require 'active_support/core_ext/string'
7
-
8
- require File.expand_path('../cloud_encrypted_sync/master', __FILE__)
5
+ require 'openssl'
6
+ require 'singleton'
7
+ require 'yaml'
9
8
 
10
9
  require File.expand_path('../cloud_encrypted_sync/adapter_liaison', __FILE__)
11
10
  require File.expand_path('../cloud_encrypted_sync/adapter_template', __FILE__)
@@ -14,4 +13,5 @@ require File.expand_path('../cloud_encrypted_sync/cryptographer', __FILE__)
14
13
  require File.expand_path('../cloud_encrypted_sync/dummy_adapter', __FILE__)
15
14
  require File.expand_path('../cloud_encrypted_sync/errors', __FILE__)
16
15
  require File.expand_path('../cloud_encrypted_sync/index', __FILE__)
17
- require File.expand_path('../cloud_encrypted_sync/progress_meter', __FILE__)
16
+ require File.expand_path('../cloud_encrypted_sync/progress_meter', __FILE__)
17
+ require File.expand_path('../cloud_encrypted_sync/synchronizer', __FILE__)
data/test/test_helper.rb CHANGED
@@ -13,30 +13,45 @@ require 'cloud_encrypted_sync'
13
13
  module CloudEncryptedSync
14
14
  class ActiveSupport::TestCase
15
15
 
16
- setup :activate_fake_fs
17
- setup :preset_environment
16
+ setup :initialize_environment
18
17
  setup :capture_stdout
19
18
  teardown :deactivate_fake_fs
20
19
  teardown :release_stdout
21
20
 
22
- def preset_environment
23
- Configuration.instance_variable_set(:@settings,nil)
24
- Configuration.instance_variable_set(:@command_line_options,nil)
25
- Index.instance_variable_set(:@local, nil)
26
- FileUtils.mkdir_p test_source_folder
27
- FileUtils.mkdir_p test_source_folder + '/test_sub_folder'
28
- File.open(test_source_folder + '/test_sub_folder/test_file_one.txt', 'w') do |test_file|
29
- test_file.write('Test File One')
30
- end
21
+ def initialize_environment
22
+ initialize_fake_fs
23
+ stub_configuration
24
+ Index.instance_variable_set(:@local,nil)
25
+ Index.instance_variable_set(:@remote,nil)
26
+ end
27
+
28
+ def stub_configuration
29
+ Configuration.stubs(:settings).returns({
30
+ :encryption_key => 'asdf',
31
+ :adapter_name => 'dummy',
32
+ :bucket => "test-bucket",
33
+ :sync_path => test_source_folder
34
+ })
35
+ Configuration.stubs(:data_folder_path).returns("#{Etc.getpwuid.dir}/.cloud_encrypted_sync")
36
+ end
37
+
38
+ def unstub_configuration
39
+ Configuration.unstub(:settings)
40
+ Configuration.unstub(:data_folder_path)
31
41
  end
32
42
 
33
43
  def test_source_folder
34
44
  @test_source_folder ||= File.expand_path('../test_folder', __FILE__)
35
45
  end
36
46
 
37
- def activate_fake_fs
47
+ def initialize_fake_fs
38
48
  FakeFS.activate!
39
49
  FakeFS::FileSystem.clear
50
+ FileUtils.mkdir_p test_source_folder
51
+ FileUtils.mkdir_p test_source_folder + '/test_sub_folder'
52
+ File.open(test_source_folder + '/test_sub_folder/test_file_one.txt', 'w') do |test_file|
53
+ test_file.write('Test File One')
54
+ end
40
55
  end
41
56
 
42
57
  def deactivate_fake_fs
@@ -3,29 +3,50 @@ require 'test_helper'
3
3
  module CloudEncryptedSync
4
4
  class AdapterLiaisonTest < ActiveSupport::TestCase
5
5
 
6
- def setup
7
- Configuration.stubs(:settings).returns({
8
- :encryption_key => 'asdf',
9
- :adapter_name => 'dummy',
10
- :bucket => "test-bucket",
11
- :sync_path => test_source_folder
12
- })
13
- Configuration.stubs(:data_folder_path).returns("#{Etc.getpwuid.dir}/.cloud_encrypted_sync")
6
+ test 'should require available adapter' do
7
+ Dir.stubs(:glob).returns(['/path/cloud_encrypted_sync_first_test_adapter-1.2.3'])
8
+ assert_raises(LoadError) { AdapterLiaison.clone.instance }
14
9
  end
15
10
 
16
11
  test 'should encrypt when writing' do
17
12
  precrypted_data = File.read(test_source_folder + '/test_sub_folder/test_file_one.txt')
18
- key = Cryptographer.hash_data('test_file_key')
19
- Adapters::Dummy.expects(:write).with(anything,key).returns(true)
20
- AdapterLiaison.instance.push(precrypted_data,key)
13
+ Cryptographer.expects(:encrypt_data).with(precrypted_data)
14
+ Adapters::Dummy.expects(:write).with(anything,'test_key')
15
+ AdapterLiaison.instance.push(precrypted_data,'test_key')
21
16
  end
22
17
 
23
18
  test 'should decrypt_when_reading' do
24
19
  precrypted_data = File.read(test_source_folder + '/test_sub_folder/test_file_one.txt')
25
20
  encrypted_data = Cryptographer.encrypt_data(precrypted_data)
26
- key = Cryptographer.hash_data('test_file_key')
27
- Adapters::Dummy.expects(:read).with(key).returns(encrypted_data)
28
- assert_equal(precrypted_data,AdapterLiaison.instance.pull(key))
21
+ Adapters::Dummy.expects(:read).with('test_key').returns(encrypted_data)
22
+ assert_equal(precrypted_data,AdapterLiaison.instance.pull('test_key'))
23
+ end
24
+
25
+ test 'should forward delete to dummy' do
26
+ Adapters::Dummy.expects(:delete).with('test_key').returns(true)
27
+ AdapterLiaison.instance.delete('test_key')
28
+ end
29
+
30
+ test 'should forward key exists to dummy' do
31
+ Adapters::Dummy.expects(:key_exists?).with('test_key').returns(true)
32
+ AdapterLiaison.instance.key_exists?('test_key')
33
+ end
34
+
35
+ test 'should find lastest versions of available adapters' do
36
+ Dir.stubs(:glob).returns([
37
+ '/path/cloud_encrypted_sync_first_test_adapter-1.2.3',
38
+ '/path/cloud_encrypted_sync_second_test_adapter-4.5.6',
39
+ '/path/cloud_encrypted_sync_second_test_adapter-7.8.9'
40
+ ])
41
+ assert_equal({'first_test' => '1.2.3', 'second_test' => '7.8.9'}, AdapterLiaison.instance.send(:latest_versions_of_installed_adapters))
42
+ end
43
+
44
+ test 'should exercise dummy adapter for test coverage' do
45
+ dummy = Adapters::Dummy
46
+ dummy.write('dummy test data','testkey')
47
+ assert_equal('dummy test data',dummy.read('testkey'))
48
+ dummy.delete('testkey')
49
+ assert_equal(false,dummy.key_exists?('testkey'))
29
50
  end
30
51
 
31
52
  end
@@ -0,0 +1,30 @@
1
+ require 'test_helper'
2
+
3
+ module CloudEncryptedSync
4
+ class AdapterTemplateTest < ActiveSupport::TestCase
5
+
6
+ test 'should register with parent class on inheritance' do
7
+ Adapters::Template.expects(:register_subclass_with_parent).returns(true)
8
+ Class.new(Adapters::Template)
9
+ end
10
+
11
+ test 'should contain registered adapters' do
12
+ assert_equal([:dummy],Adapters::Template.children.keys)
13
+ end
14
+
15
+ test 'should raise errors on public methods' do
16
+
17
+ method_argument_map = {
18
+ :parse_command_line_options => [:foo,:bar],
19
+ :write => [:foo,:bar],
20
+ :read => :foobar,
21
+ :delete => :foobar,
22
+ :key_exists? => :foobar
23
+ }
24
+ method_argument_map.each_pair do |method,arguments|
25
+ assert_raise(Errors::TemplateMethodCalled) { Adapters::Template.send(method,*arguments) }
26
+ end
27
+ end
28
+
29
+ end
30
+ end
@@ -4,19 +4,20 @@ module CloudEncryptedSync
4
4
  class ConfigurationTest < ActiveSupport::TestCase
5
5
 
6
6
  def setup
7
+ unstub_configuration
7
8
  Configuration.instance_variable_set(:@command_line_options,nil)
8
9
  Configuration.instance_variable_set(:@settings,nil)
9
10
  Configuration.instance_variable_set(:@option_parser,nil)
10
11
  Object.send(:remove_const,:ARGV) #if defined?(::ARGV)
11
12
  end
12
13
 
13
- test 'should parse command line options' do
14
+ test 'should load settings' do
14
15
  ::ARGV = '--adapter dummy --bucket foobar --data-dir ~/test/folder --encryption-key somestringofcharacters /some/path'.split(/\s/)
15
16
  settings = Configuration.settings
16
17
  assert_equal('dummy',settings[:adapter_name])
17
18
  assert_equal('~/test/folder',settings[:data_dir])
18
19
  assert_equal('somestringofcharacters',settings[:encryption_key])
19
- assert_equal('foobar',settings[:bucket_name])
20
+ assert_equal('foobar',settings[:bucket])
20
21
  end
21
22
 
22
23
  test 'should gracefully fail without path in ARGV' do
@@ -24,10 +25,17 @@ module CloudEncryptedSync
24
25
  assert_raise(Errors::IncompleteConfigurationError) { Configuration.settings }
25
26
  end
26
27
 
27
- test 'should gracefully fail when not provided encryption_key and vector provided path in ARGV' do
28
+ test 'should gracefully fail when not provided encryption_key and provided path in ARGV' do
28
29
  ::ARGV = '--adapter dummy --bucket foobar /some/path/to/sync'.split(/\s/)
29
30
  assert_raise(Errors::IncompleteConfigurationError) { Configuration.settings }
30
31
  end
31
32
 
33
+ test 'should create data folder if it does not exist' do
34
+ ::ARGV = '--adapter dummy --bucket foobar --data-dir /test --encryption-key somestringofcharacters /some/path'.split(/\s/)
35
+ assert ! File.exist?('/test')
36
+ Configuration.settings
37
+ assert File.exist?('/test')
38
+ end
39
+
32
40
  end
33
41
  end
@@ -3,31 +3,16 @@ require 'test_helper'
3
3
  module CloudEncryptedSync
4
4
  class CryptographerTest < ActiveSupport::TestCase
5
5
 
6
- def setup
7
- Configuration.stubs(:settings).returns({
8
- :encryption_key => 'asdf',
9
- :adapter_name => 'dummy',
10
- :bucket => "test-bucket",
11
- :data_dir => "#{Etc.getpwuid.dir}/.cloud_encrypted_sync",
12
- :sync_path => test_source_folder
13
- })
14
- end
15
-
16
- test 'should hash data' do
17
- hash = Cryptographer.hash_data('abc123')
18
- assert_equal('c70b5dd9ebfb6f51d09d4132b7170c9d20750a7852f00680f65658f0310e810056e6763c34c9a00b0e940076f54495c169fc2302cceb312039271c43469507dc',hash)
19
- end
20
-
21
6
  test 'should encrypt and decrypt data' do
22
- unencrypted_data = "123xyz"
23
- encrypted_data = Cryptographer.encrypt_data(unencrypted_data)
7
+ precrypted_data = "123xyz"
8
+ encrypted_data = Cryptographer.encrypt_data(precrypted_data)
24
9
  decrypted_data = Cryptographer.decrypt_data(encrypted_data)
25
- assert_equal(unencrypted_data,decrypted_data)
10
+ assert_equal(precrypted_data,decrypted_data)
26
11
  end
27
12
 
28
- test 'test should generate random key' do
29
- assert_equal(String,Cryptographer.generate_random_key.class)
13
+ test 'should hash data' do
14
+ hashed_data = Cryptographer.hash_data('abc123')
15
+ assert_equal(128,hashed_data.length)
30
16
  end
31
-
32
17
  end
33
18
  end
@@ -0,0 +1,47 @@
1
+ require 'test_helper'
2
+
3
+ module CloudEncryptedSync
4
+ class IndexTest < ActiveSupport::TestCase
5
+
6
+ test 'should compile local directory hash' do
7
+ assert_equal('',$stdout.string)
8
+ hash = Index.local
9
+ assert_equal(1,hash.keys.size)
10
+ assert_equal('test_sub_folder/test_file_one.txt',hash[hash.keys.first])
11
+ assert_match(/\% Complete/,$stdout.string)
12
+ end
13
+
14
+ test 'should fetch remote directory hash' do
15
+ AdapterLiaison.instance.expects(:pull).returns({:some => 'hash'}.to_yaml)
16
+ assert_equal({:some => 'hash'},Index.remote)
17
+ end
18
+
19
+ test 'should return empty hash if no remote index' do
20
+ Index.instance_variable_set(:@remote,nil)
21
+ AdapterLiaison.instance.expects(:pull).raises(Errors::NoSuchKey)
22
+ assert_equal({},Index.remote)
23
+ end
24
+
25
+ test 'should recompile and write local and remote hashes' do
26
+ Configuration.send(:touch_data_folder)
27
+ AdapterLiaison.instance.expects(:push)
28
+ FakeFS::File.any_instance.expects(:read).returns('Testing 123')
29
+ Index.write
30
+ end
31
+
32
+ test 'should return full normalized file path' do
33
+ assert_match(/.+\/test\/test_folder\/foobar$/,Index.full_file_path('foobar'))
34
+ end
35
+
36
+ test 'should leave sync path unchanged' do
37
+ Configuration.stubs(:settings).returns({:sync_path => '/foo/bar/'})
38
+ assert_equal('/foo/bar/',Index.send(:normalize_sync_path))
39
+ end
40
+
41
+ test 'should add trailing slash to sync path' do
42
+ Configuration.stubs(:settings).returns({:sync_path => '/foo/bar'})
43
+ assert_equal('/foo/bar/',Index.send(:normalize_sync_path))
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,169 @@
1
+ require 'test_helper'
2
+
3
+ module CloudEncryptedSync
4
+ class SynchronizerTest < ActiveSupport::TestCase
5
+
6
+ SYNC_METHODS = [:delete_local_files, :delete_remote_files, :push_files, :pull_files, :finalize]
7
+
8
+ test 'should run full sync' do
9
+ SYNC_METHODS.each { |method_name| Synchronizer.expects(method_name) }
10
+
11
+ Synchronizer.run
12
+ end
13
+
14
+ test 'should puts error message to stdout when config is incomplete' do
15
+ Configuration.stubs(:settings).raises(Errors::IncompleteConfigurationError,'test message')
16
+ AdapterLiaison.expects(:push).never
17
+ AdapterLiaison.expects(:pull).never
18
+
19
+ assert_equal('',$stdout.string)
20
+ Synchronizer.run
21
+ assert_match(/test message/,$stdout.string)
22
+ end
23
+
24
+ test 'should push files' do
25
+ Adapters::Dummy.expects(:write)
26
+
27
+ assert_equal('',$stdout.string)
28
+ Synchronizer.push_files
29
+ assert_match(/\% Complete/,$stdout.string)
30
+ end
31
+
32
+ test 'should not push files that already exist' do
33
+ AdapterLiaison.instance.stubs(:key_exists?).returns(true)
34
+ Synchronizer.push_files
35
+ assert_match(/\(already exists\)/,$stdout.string)
36
+ end
37
+
38
+ test 'should pull files' do
39
+ Index.stubs(:remote).returns({'new_file_key' => 'test_sub_folder/new_file.txt'})
40
+ Adapters::Dummy.expects(:read).with('new_file_key').returns(Cryptographer.encrypt_data('foobar'))
41
+ assert_equal('',$stdout.string)
42
+ assert_difference('Dir["#{test_source_folder}/**/*"].length') do
43
+ Synchronizer.pull_files
44
+ end
45
+ assert_match(/\% Complete/,$stdout.string)
46
+ end
47
+
48
+ test 'should not pull files that already exist' do
49
+ Synchronizer.stubs(:files_to_pull).returns({:foo => 'bar'})
50
+ File.stubs(:exist?).returns(true)
51
+ Index.stubs(:file_key).returns(:foo)
52
+ Synchronizer.pull_files
53
+ assert_match(/\(already exists\)/,$stdout.string)
54
+ end
55
+
56
+ test 'should gracefully recover if pull fails' do
57
+ Synchronizer.stubs(:files_to_pull).returns({:foo => 'bar'})
58
+ AdapterLiaison.instance.stubs(:pull).raises(Errors::NoSuchKey)
59
+ Synchronizer.pull_files
60
+ assert_match(/Failed to pull/,$stdout.string)
61
+ end
62
+
63
+ test 'should delete files from cloud' do
64
+ Index.stubs(:remote).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'})
65
+ Index.stubs(:local).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt'})
66
+ Synchronizer.stubs(:last_sync_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'})
67
+ Adapters::Dummy.expects(:delete).with('deleted_file_key').returns(true)
68
+ Synchronizer.delete_remote_files
69
+ assert_match(/Deleting Remote/,$stdout.string)
70
+ end
71
+
72
+ test 'should delete local files' do
73
+ Index.stubs(:remote).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt'})
74
+ Synchronizer.stubs(:last_sync_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt'}.merge(Index.local))
75
+ assert_difference('Dir["#{test_source_folder}/**/*"].length',-1) do
76
+ Synchronizer.delete_local_files
77
+ end
78
+ assert_match(/Deleting Local/,$stdout.string)
79
+ end
80
+
81
+ test 'should gracefully recover if local file disappears before delete' do
82
+ Synchronizer.stubs(:local_files_to_delete).returns({:foo => 'bar'})
83
+ File.stubs(:exist?).returns(false)
84
+ Synchronizer.delete_local_files
85
+ assert_match(/Not Deleting Local/,$stdout.string)
86
+ end
87
+
88
+ test 'should finalize' do
89
+ Synchronizer.instance_variable_set(:@finalize_required,true)
90
+ Index.expects(:write)
91
+
92
+ Synchronizer.finalize
93
+ end
94
+
95
+ test 'should want to push everything on first run with local files and empty remote' do
96
+ Index.stubs(:remote).returns({})
97
+ Index.stubs(:local).returns({"new_file_key"=>"test_sub_folder/new_file.txt"})
98
+ Synchronizer.stubs(:last_sync_hash).returns({})
99
+ assert_equal(Index.local,Synchronizer.send(:files_to_push))
100
+ end
101
+
102
+ test 'should want to push new files with available last sync hash' do
103
+ new_file_hash = {"new_file_key"=>"test_sub_folder/new_file.txt"}
104
+ Index.stubs(:remote).returns({"old_file_key"=>"test_sub_folder/old_file.txt"})
105
+ Index.stubs(:local).returns({"old_file_key"=>"test_sub_folder/old_file.txt"}.merge(new_file_hash))
106
+ Synchronizer.stubs(:last_sync_hash).returns({"old_file_key"=>"test_sub_folder/old_file.txt"})
107
+ assert_equal(new_file_hash,Synchronizer.send(:files_to_push))
108
+ end
109
+
110
+ test 'should want to push new files with local and remote files and empty last sync hash' do
111
+ new_file_hash = {"new_file_key"=>"test_sub_folder/new_file.txt"}
112
+ Index.stubs(:remote).returns({"old_file_key"=>"test_sub_folder/old_file.txt"})
113
+ Index.stubs(:local).returns({"old_file_key"=>"test_sub_folder/old_file.txt"}.merge(new_file_hash))
114
+ Synchronizer.stubs(:last_sync_hash).returns({})
115
+ assert_equal(new_file_hash,Synchronizer.send(:files_to_push))
116
+ end
117
+
118
+ test 'should want to puull everything on first run with no local files and remote files available' do
119
+ Index.stubs(:remote).returns({"new_file_key"=>"test_sub_folder/new_file.txt"})
120
+ Index.stubs(:local).returns({})
121
+ Synchronizer.stubs(:last_sync_hash).returns({})
122
+ assert_equal(Index.remote,Synchronizer.send(:files_to_pull))
123
+ end
124
+
125
+ test 'should want to pull new files with available last sync hash' do
126
+ new_file_hash = {"new_file_key"=>"test_sub_folder/new_file.txt"}
127
+ Index.stubs(:remote).returns({"old_file_key"=>"test_sub_folder/old_file.txt"}.merge(new_file_hash))
128
+ Index.stubs(:local).returns({"old_file_key"=>"test_sub_folder/old_file.txt"})
129
+ Synchronizer.stubs(:last_sync_hash).returns({"old_file_key"=>"test_sub_folder/old_file.txt"})
130
+ assert_equal(new_file_hash,Synchronizer.send(:files_to_pull))
131
+ end
132
+
133
+ test 'should want to pull new files with local and remote files and empty last sync hash' do
134
+ new_file_hash = {"new_file_key"=>"test_sub_folder/new_file.txt"}
135
+ Index.stubs(:remote).returns({"old_file_key"=>"test_sub_folder/old_file.txt"}.merge(new_file_hash))
136
+ Index.stubs(:local).returns({"old_file_key"=>"test_sub_folder/old_file.txt"})
137
+ Synchronizer.stubs(:last_sync_hash).returns({})
138
+ assert_equal(new_file_hash,Synchronizer.send(:files_to_pull))
139
+ end
140
+
141
+ test 'should not want to delete remote files if last sync hash is empty' do
142
+ Index.stubs(:remote).returns({"old_file_key"=>"test_sub_folder/old_file.txt"})
143
+ Index.stubs(:local).returns({})
144
+ Synchronizer.stubs(:last_sync_hash).returns({})
145
+ assert_equal({},Synchronizer.send(:remote_files_to_delete))
146
+ end
147
+
148
+ test 'should want to delete remote files' do
149
+ Index.stubs(:remote).returns({"old_file_key"=>"test_sub_folder/old_file.txt"})
150
+ Index.stubs(:local).returns({})
151
+ Synchronizer.stubs(:last_sync_hash).returns({"old_file_key"=>"test_sub_folder/old_file.txt"})
152
+ assert_equal(Index.remote,Synchronizer.send(:remote_files_to_delete))
153
+ end
154
+
155
+ test 'should not want to delete local files if last sync hash is empty' do
156
+ Index.stubs(:remote).returns({})
157
+ Index.stubs(:local).returns({"old_file_key"=>"test_sub_folder/old_file.txt"})
158
+ Synchronizer.stubs(:last_sync_hash).returns({})
159
+ assert_equal({},Synchronizer.send(:local_files_to_delete))
160
+ end
161
+
162
+ test 'should want to delete local files' do
163
+ Index.stubs(:remote).returns({})
164
+ Index.stubs(:local).returns({"old_file_key"=>"test_sub_folder/old_file.txt"})
165
+ Synchronizer.stubs(:last_sync_hash).returns({"old_file_key"=>"test_sub_folder/old_file.txt"})
166
+ assert_equal(Index.local,Synchronizer.send(:local_files_to_delete))
167
+ end
168
+ end
169
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloud_encrypted_sync
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-16 00:00:00.000000000 Z
12
+ date: 2012-11-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mocha
@@ -100,15 +100,17 @@ files:
100
100
  - lib/cloud_encrypted_sync/dummy_adapter.rb
101
101
  - lib/cloud_encrypted_sync/errors.rb
102
102
  - lib/cloud_encrypted_sync/index.rb
103
- - lib/cloud_encrypted_sync/master.rb
104
103
  - lib/cloud_encrypted_sync/progress_meter.rb
104
+ - lib/cloud_encrypted_sync/synchronizer.rb
105
105
  - lib/cloud_encrypted_sync/version.rb
106
106
  - test/test_helper.rb
107
107
  - test/unit/adapter_liaison_test.rb
108
+ - test/unit/adapter_template_test.rb
108
109
  - test/unit/configuration_test.rb
109
110
  - test/unit/cryptographer_test.rb
110
- - test/unit/master_test.rb
111
+ - test/unit/index_test.rb
111
112
  - test/unit/progress_meter_test.rb
113
+ - test/unit/synchronizer_test.rb
112
114
  homepage: https://github.com/jsgarvin/cloud_encrypted_sync
113
115
  licenses: []
114
116
  post_install_message:
@@ -136,7 +138,9 @@ summary: Encrypted sync of folder contents to/from cloud storage.
136
138
  test_files:
137
139
  - test/test_helper.rb
138
140
  - test/unit/adapter_liaison_test.rb
141
+ - test/unit/adapter_template_test.rb
139
142
  - test/unit/configuration_test.rb
140
143
  - test/unit/cryptographer_test.rb
141
- - test/unit/master_test.rb
144
+ - test/unit/index_test.rb
142
145
  - test/unit/progress_meter_test.rb
146
+ - test/unit/synchronizer_test.rb
@@ -1,138 +0,0 @@
1
- require 'test_helper'
2
-
3
- module CloudEncryptedSync
4
- class MasterTest < ActiveSupport::TestCase
5
-
6
- def setup
7
- Configuration.stubs(:settings).returns({
8
- :encryption_key => 'asdf',
9
- :adapter_name => 'dummy',
10
- :bucket => "test-bucket",
11
- :sync_path => test_source_folder
12
- })
13
- Configuration.stubs(:data_folder_path).returns("#{Etc.getpwuid.dir}/.cloud_encrypted_sync")
14
- end
15
-
16
- test 'should generate directory hash' do
17
- assert_equal('',$stdout.string)
18
- hash = Index.local
19
- assert_equal(1,hash.keys.size)
20
- assert_equal('test_sub_folder/test_file_one.txt',hash[hash.keys.first])
21
- assert_match(/\% Complete/,$stdout.string)
22
- end
23
-
24
- test 'should_return_nil_if_never_synced_before' do
25
- Master.stubs(:snapshot_file_path).returns('/non/existant/file')
26
- assert_equal(nil,Master.send(:last_sync_date))
27
- end
28
-
29
- test 'should want to push everything on first run with local files and empty remote' do
30
- Index.stubs(:remote).returns({})
31
- Index.stubs(:local).returns({"old_file_key"=>"test_sub_folder/old_file.txt"})
32
- Master.stubs(:last_sync_hash).returns({})
33
- assert_equal(Index.local,Master.send(:files_to_push))
34
- end
35
-
36
- test 'should push files' do
37
- Master.stubs(:remote_directory_hash).returns({})
38
- Master.stubs(:last_sync_hash).returns({})
39
- Adapters::Dummy.stubs(:key_exists?).returns(false)
40
- Adapters::Dummy.expects(:write).with(any_parameters).returns(true)
41
- assert_equal('',$stdout.string)
42
- Master.push_files!
43
- assert_match(/\% Complete/,$stdout.string)
44
- end
45
-
46
- test 'should want to pull everything on first run with remote files and empty local' do
47
- Index.stubs(:remote).returns({'new_file_key' => 'test_sub_folder/new_file.txt'})
48
- Index.stubs(:local).returns({})
49
- Master.stubs(:last_sync_hash).returns({})
50
- assert_equal({'new_file_key' => 'test_sub_folder/new_file.txt'},Master.send(:files_to_pull))
51
- end
52
-
53
- test 'should pull files' do
54
- Index.stubs(:remote).returns({'new_file_key' => 'test_sub_folder/new_file.txt'})
55
- Index.stubs(:local).returns({})
56
- Master.stubs(:last_sync_hash).returns({})
57
- Adapters::Dummy.expects(:read).with('new_file_key').returns(Cryptographer.encrypt_data('foobar'))
58
- assert_equal('',$stdout.string)
59
- assert_difference('Dir["#{test_source_folder}/**/*"].length') do
60
- Master.pull_files!
61
- end
62
- assert_match(/\% Complete/,$stdout.string)
63
- end
64
-
65
- test 'should only want to push new files on later run' do
66
- Index.stubs(:remote).returns({'old_file_key' => 'test_sub_folder/old_file.txt'})
67
- Index.stubs(:local).returns({'new_file_key' => 'test_sub_folder/new_file.txt', 'old_file_key' => 'test_sub_folder/old_file.txt'})
68
- Master.stubs(:last_sync_hash).returns({'old_file_key' => 'test_sub_folder/old_file.txt'})
69
- assert_equal({'new_file_key' => 'test_sub_folder/new_file.txt'},Master.send(:files_to_push))
70
- end
71
-
72
- test 'should want to pull new files from cloud' do
73
- Index.stubs(:remote).returns({'new_file_key' => 'test_sub_folder/new_file.txt', 'old_file_key' => 'test_sub_folder/old_file.txt'})
74
- Index.stubs(:local).returns({'old_file_key' => 'test_sub_folder/old_file.txt'})
75
- Master.stubs(:last_sync_hash).returns({'old_file_key' => 'test_sub_folder/old_file.txt'})
76
- assert_equal({'new_file_key' => 'test_sub_folder/new_file.txt'},Master.send(:files_to_pull))
77
- end
78
-
79
- test 'should want to delete locally missing files from cloud' do
80
- Index.stubs(:remote).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'})
81
- Index.stubs(:local).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt'})
82
- Master.stubs(:last_sync_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'})
83
- assert_equal({'deleted_file_key' => 'test_sub_folder/deleted_file.txt'},Master.send(:remote_files_to_delete))
84
- end
85
-
86
- test 'should delete files from cloud' do
87
- Index.stubs(:remote).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'})
88
- Index.stubs(:local).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt'})
89
- Master.stubs(:last_sync_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'})
90
- Adapters::Dummy.expects(:delete).with('deleted_file_key').returns(true)
91
- Master.delete_remote_files!
92
- end
93
-
94
- test 'should want to delete appropriate files locally' do
95
- Index.stubs(:remote).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt'})
96
- Index.stubs(:local).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'})
97
- Master.stubs(:last_sync_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'})
98
- assert_equal({'deleted_file_key' => 'test_sub_folder/deleted_file.txt'},Master.send(:local_files_to_delete))
99
- end
100
-
101
- test 'should delete local files' do
102
- Index.stubs(:remote).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt'})
103
- Master.stubs(:last_sync_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'}.merge(Index.local))
104
- assert_difference('Dir["#{test_source_folder}/**/*"].length',-1) do
105
- Master.delete_local_files!
106
- end
107
- end
108
-
109
- test 'should finalize' do
110
- FileUtils.mkdir_p(Configuration.data_folder_path)
111
- sample_directory_hash = {'sample_file_key' => 'test_sub_folder/sample_file.txt'}
112
- Master.instance_variable_set(:@finalize_required,true)
113
- Master.stubs(:directory_hash).returns(sample_directory_hash)
114
- Adapters::Dummy.expects(:write).with(anything,Index.send(:index_key)).returns(true)
115
- Master.finalize!
116
- end
117
-
118
- test 'should decrypt remote directory file' do
119
- #setup mock data
120
- sample_directory_hash = {'sample_file_key' => 'test_sub_folder/sample_file.txt'}
121
- encrypted_directory_hash = Cryptographer.encrypt_data(sample_directory_hash.to_yaml)
122
- Adapters::Dummy.expects(:read).with(Index.send(:index_key)).returns(encrypted_directory_hash)
123
-
124
- #do actual test
125
- decrypted_remote_hash = Index.remote
126
- assert_equal(sample_directory_hash,decrypted_remote_hash)
127
- end
128
-
129
- test 'should puts error message to stdout' do
130
- Configuration.stubs(:settings).raises(Errors::IncompleteConfigurationError,'test message')
131
- assert_equal('',$stdout.string)
132
- Master.expects(:pull_files).never
133
- Master.sync
134
- assert_match(/test message/,$stdout.string)
135
- end
136
-
137
- end
138
- end