cloud_encrypted_sync 0.1.1 → 0.1.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.
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