store_agent 1.0.0

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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +4 -0
  5. data/Guardfile +5 -0
  6. data/LICENSE +202 -0
  7. data/README.md +433 -0
  8. data/Rakefile +2 -0
  9. data/lib/store_agent.rb +31 -0
  10. data/lib/store_agent/config.rb +152 -0
  11. data/lib/store_agent/data_encoder.rb +32 -0
  12. data/lib/store_agent/data_encoder/gzip_encoder.rb +50 -0
  13. data/lib/store_agent/data_encoder/openssl_aes_256_cbc_encoder.rb +65 -0
  14. data/lib/store_agent/exceptions.rb +87 -0
  15. data/lib/store_agent/node.rb +26 -0
  16. data/lib/store_agent/node/attachment.rb +93 -0
  17. data/lib/store_agent/node/attachment/metadata.rb +120 -0
  18. data/lib/store_agent/node/attachment/permission.rb +121 -0
  19. data/lib/store_agent/node/object.rb +233 -0
  20. data/lib/store_agent/node/object/directory_object.rb +264 -0
  21. data/lib/store_agent/node/object/file_object.rb +197 -0
  22. data/lib/store_agent/node/object/virtual_object.rb +25 -0
  23. data/lib/store_agent/node/prepend_module/locker.rb +125 -0
  24. data/lib/store_agent/node/prepend_module/path_validator.rb +138 -0
  25. data/lib/store_agent/node/prepend_module/permission_checker.rb +96 -0
  26. data/lib/store_agent/user.rb +111 -0
  27. data/lib/store_agent/validator.rb +60 -0
  28. data/lib/store_agent/version.rb +19 -0
  29. data/lib/store_agent/version_manager.rb +101 -0
  30. data/lib/store_agent/version_manager/ruby_git.rb +100 -0
  31. data/lib/store_agent/version_manager/rugged_git.rb +133 -0
  32. data/lib/store_agent/workspace.rb +88 -0
  33. data/spec/spec_helper.rb +47 -0
  34. data/spec/store_agent/data_encoder/encoder_shared_context.rb +74 -0
  35. data/spec/store_agent/data_encoder/gzip_encoder_spec.rb +41 -0
  36. data/spec/store_agent/data_encoder/openssl_aes_256_cbc_encoder_spec.rb +42 -0
  37. data/spec/store_agent/data_encoder_spec.rb +78 -0
  38. data/spec/store_agent/node/directory_object_spec.rb +563 -0
  39. data/spec/store_agent/node/file_object_spec.rb +379 -0
  40. data/spec/store_agent/node/locker_spec.rb +191 -0
  41. data/spec/store_agent/node/metadata_spec.rb +339 -0
  42. data/spec/store_agent/node/object_spec.rb +73 -0
  43. data/spec/store_agent/node/path_validator_spec.rb +121 -0
  44. data/spec/store_agent/node/permission_spec.rb +232 -0
  45. data/spec/store_agent/user_spec.rb +127 -0
  46. data/spec/store_agent/version_manager/git_shared_context.rb +286 -0
  47. data/spec/store_agent/version_manager/ruby_git_spec.rb +32 -0
  48. data/spec/store_agent/version_manager/rugged_git_spec.rb +32 -0
  49. data/spec/store_agent/workspace_spec.rb +107 -0
  50. data/spec/store_agent_spec.rb +53 -0
  51. data/store_agent.gemspec +33 -0
  52. metadata +252 -0
@@ -0,0 +1,47 @@
1
+ require "simplecov"
2
+
3
+ SimpleCov.start do
4
+ require "bundler"
5
+ add_filter Bundler::bundle_path.to_s
6
+ add_filter File.dirname(__FILE__)
7
+ end
8
+
9
+ require "store_agent"
10
+
11
+ RSpec.configure do |config|
12
+ config.expect_with :rspec do |expectations|
13
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
14
+ end
15
+
16
+ config.mock_with :rspec do |mocks|
17
+ mocks.verify_partial_doubles = true
18
+ end
19
+
20
+ config.filter_run :focus
21
+ config.run_all_when_everything_filtered = true
22
+
23
+ config.disable_monkey_patching!
24
+
25
+ config.profile_examples = 10
26
+ config.order = :random
27
+ # config.order = "16118"
28
+ # config.fail_fast = true
29
+
30
+ Kernel.srand config.seed
31
+
32
+ config.before(:suite) do
33
+ StoreAgent.configure do |config|
34
+ config.json_indent_level = 2
35
+ config.default_owner_permission = {
36
+ "read" => true,
37
+ "write" => true,
38
+ "execute" => true,
39
+ # "chown" => true,
40
+ "chmod" => true
41
+ }
42
+ end
43
+ if File.exists?(StoreAgent.config.storage_root)
44
+ FileUtils.remove_dir(StoreAgent.config.storage_root)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,74 @@
1
+ #--
2
+ # Copyright 2015 realglobe, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #++
16
+
17
+ RSpec.shared_context "encoder" do
18
+ after :all do
19
+ StoreAgent.configure do |c|
20
+ c.storage_data_encoders = []
21
+ c.attachment_data_encoders = []
22
+ end
23
+ end
24
+ let :user do
25
+ StoreAgent::User.new("foo")
26
+ end
27
+ let :workspace do
28
+ user.workspace(workspace_name)
29
+ end
30
+ before do
31
+ if !workspace.exists?
32
+ workspace.create
33
+ workspace.directory("foo").create
34
+ workspace.file("foo/bar.txt").create("テスト")
35
+ end
36
+ end
37
+
38
+ context "encode/decode のテスト" do
39
+ it "バイナリ文字列を encode してから decode すると、元の文字列に戻る" do
40
+ 100.times.each do
41
+ string = OpenSSL::Random.random_bytes(256)
42
+ string.force_encoding("UTF-8")
43
+ encoded_string = encoder.encode(string)
44
+ expect(encoder.decode(encoded_string)).to eq string
45
+ end
46
+ end
47
+ it "日本語文字列を encode してから decode すると、元の文字列に戻る" do
48
+ 100.times.each do
49
+ string = "日本語を含むUTF-8文字列"
50
+ encoded_string = encoder.encode(string)
51
+ expect(encoder.decode(encoded_string)).to eq string
52
+ end
53
+ end
54
+ end
55
+ context "作成されたファイルのテスト" do
56
+ it "メタデータはエンコードされているので、JSON 形式ではない" do
57
+ encoded_data = open(workspace.root.metadata.file_path).read
58
+ expect do
59
+ Oj.load(encoded_data)
60
+ end.to raise_error
61
+ end
62
+ it "パーミッション情報はエンコードされているので、JSON 形式ではない" do
63
+ encoded_data = open(workspace.directory("foo").permission.file_path).read
64
+ expect do
65
+ Oj.load(encoded_data)
66
+ end.to raise_error
67
+ end
68
+ it "ファイルはエンコードされている" do
69
+ encoded_data = open(workspace.file("foo/bar.txt").storage_object_path).read
70
+ expect(encoded_data).to_not eq "テスト"
71
+ expect(workspace.file("foo/bar.txt").read).to eq "テスト"
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,41 @@
1
+ #--
2
+ # Copyright 2015 realglobe, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #++
16
+
17
+ require "spec_helper"
18
+ require "store_agent/data_encoder/encoder_shared_context"
19
+
20
+ RSpec.describe StoreAgent::DataEncoder::GzipEncoder do
21
+ before :all do
22
+ StoreAgent.configure do |c|
23
+ c.storage_data_encoders = [StoreAgent::DataEncoder::GzipEncoder.new]
24
+ c.attachment_data_encoders = [StoreAgent::DataEncoder::GzipEncoder.new]
25
+ end
26
+ end
27
+ let :workspace_name do
28
+ "gzip_encoder_test"
29
+ end
30
+ let :encoder do
31
+ StoreAgent::DataEncoder::GzipEncoder.new
32
+ end
33
+
34
+ include_context "encoder"
35
+
36
+ it "データは gzip ... でエンコードされた形式で保存される" do
37
+ file = workspace.file("enc_data").create("encode のテスト")
38
+ command = "gunzip -c #{file.storage_object_path}"
39
+ expect(`#{command}`).to eq "encode のテスト"
40
+ end
41
+ end
@@ -0,0 +1,42 @@
1
+ #--
2
+ # Copyright 2015 realglobe, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #++
16
+
17
+ require "spec_helper"
18
+ require "store_agent/data_encoder/encoder_shared_context"
19
+
20
+ RSpec.describe StoreAgent::DataEncoder::OpensslAes256CbcEncoder do
21
+ before :all do
22
+ StoreAgent.configure do |c|
23
+ c.storage_data_encoders = [StoreAgent::DataEncoder::OpensslAes256CbcEncoder.new]
24
+ c.attachment_data_encoders = [StoreAgent::DataEncoder::OpensslAes256CbcEncoder.new]
25
+ end
26
+ end
27
+ let :workspace_name do
28
+ "openssl_aes_256_cbc_encoder_test"
29
+ end
30
+ let :encoder do
31
+ StoreAgent::DataEncoder::OpensslAes256CbcEncoder.new
32
+ end
33
+
34
+ include_context "encoder"
35
+
36
+ it "データは openssl enc -e -aes-256-cbc ... でエンコードされた形式で保存される" do
37
+ password = ENV["STORE_AGENT_DATA_ENCODER_PASSWORD"] || ""
38
+ file = workspace.file("enc_data").create("encode のテスト")
39
+ command = "openssl enc -d -aes-256-cbc -in #{file.storage_object_path} -k '#{password}'"
40
+ expect(`#{command}`).to eq "encode のテスト"
41
+ end
42
+ end
@@ -0,0 +1,78 @@
1
+ #--
2
+ # Copyright 2015 realglobe, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #++
16
+
17
+ require "spec_helper"
18
+
19
+ RSpec.describe StoreAgent::DataEncoder do
20
+ before :all do
21
+ StoreAgent.configure do |c|
22
+ c.storage_data_encoders = [] <<
23
+ StoreAgent::DataEncoder::GzipEncoder.new <<
24
+ StoreAgent::DataEncoder::OpensslAes256CbcEncoder.new
25
+ c.attachment_data_encoders = [] <<
26
+ StoreAgent::DataEncoder::GzipEncoder.new <<
27
+ StoreAgent::DataEncoder::OpensslAes256CbcEncoder.new
28
+ end
29
+ end
30
+ after :all do
31
+ StoreAgent.configure do |c|
32
+ c.storage_data_encoders = []
33
+ c.attachment_data_encoders = []
34
+ end
35
+ end
36
+ let :user do
37
+ StoreAgent::User.new("foo")
38
+ end
39
+ let :workspace do
40
+ user.workspace("gzip_and_openssl_encoder_test")
41
+ end
42
+ let :password do
43
+ ENV["STORE_AGENT_DATA_ENCODER_PASSWORD"] || ""
44
+ end
45
+ before do
46
+ if !workspace.exists?
47
+ workspace.create
48
+ workspace.directory("foo").create
49
+ workspace.file("foo/bar.txt").create("encode のテスト")
50
+ end
51
+ end
52
+
53
+ it "データは gzip -c ... | openssl ... でエンコードされた形式で保存される" do
54
+ file = workspace.file("foo/bar.txt")
55
+ command = "openssl enc -d -aes-256-cbc -in #{file.storage_object_path} -k '#{password}' | gunzip"
56
+ expect(`#{command}`).to eq "encode のテスト"
57
+ end
58
+ it "メタデータは gzip -c ... | openssl ... でエンコードされた形式で保存される" do
59
+ dir = workspace.directory("foo")
60
+ command = "openssl enc -d -aes-256-cbc -in #{dir.metadata.file_path} -k '#{password}' | gunzip"
61
+ expect do
62
+ Oj.load(`#{command}`)
63
+ end.to_not raise_error
64
+ end
65
+ it "メタデータのファイルサイズはエンコード前のサイズ" do
66
+ dir = workspace.directory("bitesize_test").create
67
+ file = workspace.file("bitesize_test/hoge.txt").create("12 byte file")
68
+ expect(file.metadata["bytes"]).to eq 12
69
+ expect(workspace.directory("bitesize_test").metadata.disk_usage).to eq 4108
70
+ end
71
+ it "パーミッション情報は gzip -c ... | openssl ... でエンコードされた形式で保存される" do
72
+ file = workspace.file("foo/bar.txt")
73
+ command = "openssl enc -d -aes-256-cbc -in #{file.permission.file_path} -k '#{password}' | gunzip"
74
+ expect do
75
+ Oj.load(`#{command}`)
76
+ end.to_not raise_error
77
+ end
78
+ end
@@ -0,0 +1,563 @@
1
+ #--
2
+ # Copyright 2015 realglobe, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #++
16
+
17
+ require "spec_helper"
18
+
19
+ RSpec.describe StoreAgent::Node::DirectoryObject do
20
+ let :user do
21
+ StoreAgent::User.new("foo")
22
+ end
23
+ let :workspace do
24
+ user.workspace(workspace_name)
25
+ end
26
+ before do
27
+ workspace.create if !workspace.exists?
28
+ end
29
+
30
+ context "初期化時に path の末尾に / を追加する" do
31
+ let :workspace_name do
32
+ "test_dir_workspace_initialize"
33
+ end
34
+ let :directory do
35
+ workspace.directory(@path)
36
+ end
37
+
38
+ it ". は / に変換される" do
39
+ @path = "."
40
+ expect(directory.path).to eq "/"
41
+ end
42
+ it "/././. は / に変換される" do
43
+ @path = "/././."
44
+ expect(directory.path).to eq "/"
45
+ end
46
+ it "foo は /foo/ に変換される" do
47
+ @path = "foo"
48
+ expect(directory.path).to eq "/foo/"
49
+ end
50
+ it "///foo///bar/// は /foo/bar/ に変換される" do
51
+ @path = "///foo///bar///"
52
+ expect(directory.path).to eq "/foo/bar/"
53
+ end
54
+ it "../foo/bar は /foo/bar/ に変換される" do
55
+ @path = "../foo/bar"
56
+ expect(directory.path).to eq "/foo/bar/"
57
+ end
58
+ end
59
+
60
+ context "ディレクトリ作成のテスト" do
61
+ let :workspace_name do
62
+ "test_dir_workspace_create"
63
+ end
64
+ before do
65
+ dir = workspace.directory("/foo")
66
+ if !dir.exists?
67
+ dir.create
68
+ workspace.file("/bar").create
69
+ end
70
+ end
71
+
72
+ it "既に同名のディレクトリがある場合、作成できない" do
73
+ expect do
74
+ workspace.directory("/foo").create
75
+ end.to raise_error
76
+ end
77
+ it "既に同名のファイルがある場合、作成できない" do
78
+ expect do
79
+ workspace.directory("/bar").create
80
+ end.to raise_error
81
+ end
82
+ it "ディレクトリ名がメタデータの拡張子で終わる場合、作成できない" do
83
+ expect do
84
+ workspace.directory("/hoge.meta").create
85
+ end.to raise_error
86
+ end
87
+ it "ディレクトリ名がパーミッションデータの拡張子で終わる場合、作成できない" do
88
+ expect do
89
+ workspace.directory("/hoge.perm").create
90
+ end.to raise_error
91
+ end
92
+ it "パスに問題が無ければディレクトリ作成できる" do
93
+ workspace.directory("hoge").create
94
+ expect(workspace.directory("hoge").exists?).to be true
95
+ workspace.directory("fuga").create
96
+ expect(workspace.directory("fuga").exists?).to be true
97
+ workspace.directory(".git").create
98
+ expect(workspace.directory(".git").exists?).to be true
99
+ workspace.directory(".keep").create
100
+ expect(workspace.directory(".keep").exists?).to be true
101
+ end
102
+ it "引数にブロックを渡してdirectory_bytes_limitを初期化できる" do
103
+ pending
104
+
105
+ workspace.directory("limit").create do |d|
106
+ d..metadata["directory_bytes_limit"] = 12345678
107
+ end
108
+ expect(workspace.directory("limit").metadata["directory_bytes_limit"]).to eq 12345678
109
+ end
110
+ it "引数にブロックを渡してオーナーを初期化できる" do
111
+ skip "とりあえず保留"
112
+
113
+ StoreAgent::Superuser.new.workspace(workspace_name).directory("chown").create do |d|
114
+ d.initial_owner = "initial_owner"
115
+ end
116
+ expect(workspace.directory("chown").metadata["owner"]).to eq "initial_owner"
117
+ end
118
+ it "引数にブロックを渡して権限を初期化できる" do
119
+ pending
120
+
121
+ workspace.directory("chmod").create do |d|
122
+ d.initial_permission = {"readwrite" => true}
123
+ end
124
+ expect(workspace.directory("chmod").permission).to eq ({"readwrite" => true})
125
+ end
126
+ end
127
+
128
+ context "ディレクトリ削除のテスト" do
129
+ let :workspace_name do
130
+ "test_dir_workspace_delete"
131
+ end
132
+ before do
133
+ dir = workspace.directory("/delete_test")
134
+ if dir.exists?
135
+ StoreAgent::Superuser.new.workspace(workspace_name).directory("/delete_test").delete
136
+ end
137
+ workspace.directory("/delete_test").create
138
+ workspace.directory("/delete_test/foo").create
139
+ workspace.file("/delete_test/hoge.txt").create
140
+ end
141
+
142
+ context "配下の全オブジェクトに対して削除権限がある場合" do
143
+ before do
144
+ workspace.directory("/delete_test").delete
145
+ end
146
+
147
+ it "ルートノードは削除できない" do
148
+ expect do
149
+ workspace.directory("/").delete
150
+ end.to raise_error
151
+ end
152
+ it "ディレクトリが丸ごと削除される" do
153
+ expect(workspace.directory("/delete_test").exists?).to be false
154
+ end
155
+ it "メタデータ側のディレクトリは削除される" do
156
+ metadata_dir_path = "/tmp/store_agent/test_dir_workspace_delete/metadata/delete_test"
157
+ expect(File.exists?(metadata_dir_path)).to be false
158
+ end
159
+ it "パーミッション側のディレクトリは削除される" do
160
+ permission_dir_path = "/tmp/store_agent/test_dir_workspace_delete/permission/delete_test"
161
+ expect(File.exists?(permission_dir_path)).to be false
162
+ end
163
+ end
164
+ context "配下に削除権限が無いオブジェクトがある場合" do
165
+ let :workspace_name do
166
+ "test_dir_workspace_delete_with_no_permission"
167
+ end
168
+ before do
169
+ workspace.directory("/delete_test/bar").create
170
+ workspace.file("/delete_test/bar/hoge.txt").create("1234567890")
171
+ workspace.file("/delete_test/bar/hoge.txt").unset_permission(identifier: "foo", permission_names: "write")
172
+ workspace.directory("/delete_test/foobar").create
173
+ workspace.file("/delete_test/foobar/fuga.txt").create("12345678901234567890")
174
+ workspace.directory("/delete_test/foobar").unset_permission(identifier: "foo", permission_names: "write")
175
+ begin
176
+ workspace.directory("/delete_test").delete
177
+ rescue
178
+ end
179
+ end
180
+
181
+ context "メタデータの中身" do
182
+ context "ルートディレクトリ" do
183
+ let :root_node do
184
+ workspace.directory("/")
185
+ end
186
+
187
+ it "ディスク使用量は 4096 * 4 + 30 バイト" do
188
+ expect(root_node.metadata.disk_usage).to eq ((4096 * 4) + 30)
189
+ end
190
+ it "直下のファイル数は 1" do
191
+ expect(root_node.directory_file_count).to eq 1
192
+ end
193
+ it "サブツリー全体の配下ファイル数は 5" do
194
+ expect(root_node.tree_file_count).to eq 5
195
+ end
196
+ end
197
+ context "中間ディレクトリ" do
198
+ let :dir do
199
+ workspace.directory("/delete_test")
200
+ end
201
+
202
+ it "ディスク使用量は 4096 * 3 + 30 バイト" do
203
+ expect(dir.metadata.disk_usage).to eq ((4096 * 3) + 30)
204
+ end
205
+ it "直下のファイル数は 2" do
206
+ expect(dir.directory_file_count).to eq 2
207
+ end
208
+ it "サブツリー全体の配下ファイル数は 4" do
209
+ expect(dir.tree_file_count).to eq 4
210
+ end
211
+ end
212
+ context "配下ファイルに削除権限が無いディレクトリ" do
213
+ let :dir do
214
+ workspace.directory("/delete_test/bar")
215
+ end
216
+
217
+ it "ディスク使用量は 4096 + 10 バイト" do
218
+ expect(dir.metadata.disk_usage).to eq (4096 + 10)
219
+ end
220
+ it "直下のファイル数は 1" do
221
+ expect(dir.directory_file_count).to eq 1
222
+ end
223
+ it "サブツリー全体の配下ファイル数は 1" do
224
+ expect(dir.tree_file_count).to eq 1
225
+ end
226
+ end
227
+ context "削除権限が無いディレクトリ" do
228
+ let :dir do
229
+ workspace.directory("/delete_test/foobar")
230
+ end
231
+
232
+ it "ディスク使用量は 4096 + 20 バイト" do
233
+ expect(dir.metadata.disk_usage).to eq (4096 + 20)
234
+ end
235
+ it "直下のファイル数は 1" do
236
+ expect(dir.directory_file_count).to eq 1
237
+ end
238
+ it "サブツリー全体の配下ファイル数は 1" do
239
+ expect(dir.tree_file_count).to eq 1
240
+ end
241
+ end
242
+ end
243
+ context "削除権限があるノード" do
244
+ it "ディレクトリが丸ごと削除される" do
245
+ expect(workspace.directory("/delete_test/foo").exists?).to be false
246
+ end
247
+ it "メタデータ側のディレクトリは削除される" do
248
+ metadata_dir_path = "/tmp/store_agent/test_dir_workspace_delete_with_no_permission/metadata/delete_test/foo"
249
+ expect(File.exists?(metadata_dir_path)).to be false
250
+ end
251
+ it "パーミッション側のディレクトリは削除される" do
252
+ permission_dir_path = "/tmp/store_agent/test_dir_workspace_delete_with_no_permission/permission/delete_test/foo"
253
+ expect(File.exists?(permission_dir_path)).to be false
254
+ end
255
+ end
256
+ context "サブツリー内に削除権限が無いファイルがある場合" do
257
+ it "ファイルと親ディレクトリは削除されずに残る" do
258
+ expect(workspace.directory("/delete_test/bar").exists?).to be true
259
+ expect(workspace.file("/delete_test/bar/hoge.txt").exists?).to be true
260
+ end
261
+ it "ファイルと親ディレクトリのメタデータは削除されない" do
262
+ dir_metadata_path = "/tmp/store_agent/test_dir_workspace_delete_with_no_permission/metadata/delete_test/bar/.meta"
263
+ file_metadata_path = "/tmp/store_agent/test_dir_workspace_delete_with_no_permission/metadata/delete_test/bar/hoge.txt.meta"
264
+ expect(File.exists?(dir_metadata_path)).to be true
265
+ expect(File.exists?(file_metadata_path)).to be true
266
+ end
267
+ it "ファイルと親ディレクトリのパーミッションは削除されない" do
268
+ dir_permission_path = "/tmp/store_agent/test_dir_workspace_delete_with_no_permission/permission/delete_test/bar/.perm"
269
+ file_permission_path = "/tmp/store_agent/test_dir_workspace_delete_with_no_permission/permission/delete_test/bar/hoge.txt.perm"
270
+ expect(File.exists?(dir_permission_path)).to be true
271
+ expect(File.exists?(file_permission_path)).to be true
272
+ end
273
+ end
274
+ context "サブツリー内に削除権限が無いディレクトリがある場合" do
275
+ it "ディレクトリと配下ファイルは削除されずに残る" do
276
+ expect(workspace.directory("/delete_test/foobar").exists?).to be true
277
+ expect(workspace.file("/delete_test/foobar/fuga.txt").exists?).to be true
278
+ end
279
+ it "ディレクトリと配下ファイルのメタデータは削除されない" do
280
+ dir_metadata_path = "/tmp/store_agent/test_dir_workspace_delete_with_no_permission/metadata/delete_test/foobar/.meta"
281
+ file_metadata_path = "/tmp/store_agent/test_dir_workspace_delete_with_no_permission/metadata/delete_test/foobar/fuga.txt.meta"
282
+ expect(File.exists?(dir_metadata_path)).to be true
283
+ expect(File.exists?(file_metadata_path)).to be true
284
+ end
285
+ it "ディレクトリと配下ファイルのパーミッションは削除されない" do
286
+ dir_permission_path = "/tmp/store_agent/test_dir_workspace_delete_with_no_permission/permission/delete_test/foobar/.perm"
287
+ file_permission_path = "/tmp/store_agent/test_dir_workspace_delete_with_no_permission/permission/delete_test/foobar/fuga.txt.perm"
288
+ expect(File.exists?(dir_permission_path)).to be true
289
+ expect(File.exists?(file_permission_path)).to be true
290
+ end
291
+ end
292
+ end
293
+ end
294
+
295
+ context "ディレクトリのコピー" do
296
+ let :workspace_name do
297
+ "test_dir_workspace_copy"
298
+ end
299
+ let :src_dir do
300
+ workspace.directory("copy/src")
301
+ end
302
+ before do
303
+ dir = workspace.directory("copy")
304
+ if !dir.exists?
305
+ dir.create
306
+ src_dir.create
307
+ src_dir.file("foo.txt").create("copy")
308
+ end
309
+ end
310
+
311
+ context "コピー先にオブジェクトが無い場合" do
312
+ it "ディレクトリが中身ごとコピーされ、メタデータが更新される" do
313
+ dest_path = "copy/dest"
314
+
315
+ prev_count = workspace.directory("copy").tree_file_count
316
+ src_dir.copy(dest_path)
317
+ expect(workspace.file("copy/dest/foo.txt").read).to eq "copy"
318
+ expect(workspace.directory("copy").tree_file_count).to eq prev_count + 2
319
+ end
320
+ end
321
+ context "コピー先にファイルが存在する場合" do
322
+ it "例外 InvalidNodeTypeError が発生する" do
323
+ dest_file_path = "copy/dest_file.txt"
324
+ workspace.file(dest_file_path).create("dest file")
325
+
326
+ expect do
327
+ src_dir.copy(dest_file_path)
328
+ end.to raise_error StoreAgent::InvalidNodeTypeError
329
+ end
330
+ end
331
+ context "コピー先にディレクトリが存在する場合" do
332
+ it "コピー先のディレクトリ内に同名のオブジェクトが存在しない場合、ディレクトリがコピーされる" do
333
+ dest_directory_path = "copy/dest_dir"
334
+ workspace.directory(dest_directory_path).create
335
+
336
+ prev_count = workspace.directory("copy").tree_file_count
337
+ prev_bytesize = workspace.directory("copy").metadata["directory_bytes"]
338
+ src_dir.copy(dest_directory_path)
339
+ expect(workspace.directory(dest_directory_path).file("src/foo.txt").read).to eq "copy"
340
+ expect(workspace.directory("copy").metadata["directory_bytes"]).to eq prev_bytesize + 4096 + 4
341
+ expect(workspace.directory("copy").tree_file_count).to eq prev_count + 2
342
+ end
343
+ it "コピー先のディレクトリ内に同名のファイルが存在する場合、例外が発生する" do
344
+ dest_directory_path = "copy/dest_exists_file"
345
+ workspace.directory(dest_directory_path).create
346
+ workspace.directory(dest_directory_path).file("src").create("file already exists")
347
+
348
+ expect do
349
+ src_dir.copy(dest_directory_path)
350
+ end.to raise_error StoreAgent::InvalidPathError
351
+ end
352
+ it "コピー先のディレクトリ内に同名のディレクトリが存在する場合、例外が発生する" do
353
+ dest_directory_path = "copy/dest_exists_dir"
354
+ workspace.directory(dest_directory_path).create
355
+ workspace.directory(dest_directory_path).directory("src").create
356
+
357
+ expect do
358
+ src_dir.copy(dest_directory_path)
359
+ end.to raise_error StoreAgent::InvalidPathError
360
+ end
361
+ end
362
+ end
363
+
364
+ context "ディレクトリの移動" do
365
+ let :workspace_name do
366
+ "test_dir_workspace_move"
367
+ end
368
+ let :directory do
369
+ workspace.directory("move")
370
+ end
371
+ let :src_dir do
372
+ workspace.directory("move/src")
373
+ end
374
+ before do
375
+ if !directory.exists?
376
+ directory.create
377
+ end
378
+ if !src_dir.exists?
379
+ src_dir.create
380
+ workspace.file("move/src/bar.txt").create("move")
381
+ end
382
+ end
383
+
384
+ context "移動先にオブジェクトが無い場合" do
385
+ it "ディレクトリが中身ごと移動され、メタデータが更新される" do
386
+ dest_path = "move/dest"
387
+
388
+ prev_count = workspace.directory("move").tree_file_count
389
+ src_dir.move(dest_path)
390
+ expect(workspace.file("move/dest/bar.txt").read).to eq "move"
391
+ expect(workspace.directory("move").tree_file_count).to eq prev_count
392
+ end
393
+ end
394
+ context "移動先にファイルが存在する場合" do
395
+ it "例外 InvalidNodeTypeError が発生する" do
396
+ dest_file_path = "move/dest_file.txt"
397
+ workspace.file(dest_file_path).create("dest file")
398
+
399
+ expect do
400
+ src_dir.move(dest_file_path)
401
+ end.to raise_error StoreAgent::InvalidNodeTypeError
402
+ end
403
+ end
404
+ context "移動先にディレクトリが存在する場合" do
405
+ it "移動先のディレクトリ内に同名のオブジェクトが存在しない場合、ディレクトリが移動される" do
406
+ dest_directory_path = "move/dest_dir"
407
+ workspace.directory(dest_directory_path).create
408
+
409
+ prev_count = workspace.directory("move").tree_file_count
410
+ prev_bytesize = workspace.directory("move").metadata["directory_bytes"]
411
+ src_dir.move(dest_directory_path)
412
+ expect(workspace.directory(dest_directory_path).file("src/bar.txt").read).to eq "move"
413
+ expect(workspace.directory("move").metadata["directory_bytes"]).to eq prev_bytesize
414
+ expect(workspace.directory("move").tree_file_count).to eq prev_count
415
+ end
416
+ it "移動先のディレクトリ内に同名のファイルが存在する場合、例外が発生する" do
417
+ dest_directory_path = "move/dest_exists_file"
418
+ workspace.directory(dest_directory_path).create
419
+ workspace.directory(dest_directory_path).file("src").create("file already exists")
420
+
421
+ expect do
422
+ src_dir.move(dest_directory_path)
423
+ end.to raise_error StoreAgent::InvalidPathError
424
+ end
425
+ it "移動先のディレクトリ内に同名のディレクトリが存在する場合、例外が発生する" do
426
+ dest_directory_path = "move/dest_exists_dir"
427
+ workspace.directory(dest_directory_path).create
428
+ workspace.directory(dest_directory_path).directory("src").create
429
+
430
+ expect do
431
+ src_dir.move(dest_directory_path)
432
+ end.to raise_error StoreAgent::InvalidPathError
433
+ end
434
+ end
435
+ end
436
+
437
+ context "メタデータ取得のテスト" do
438
+ let :workspace_name do
439
+ "test_dir_workspace_get_metadata"
440
+ end
441
+ it "get_metadata でメタデータをハッシュ形式で取得できる" do
442
+ expect(workspace.directory("/").get_metadata.class).to eq Hash
443
+ end
444
+ end
445
+
446
+ context "パーミッション情報取得のテスト" do
447
+ let :workspace_name do
448
+ "test_dir_workspace_get_permissions"
449
+ end
450
+ it "get_permissions でパーミッション情報をハッシュ形式で取得できる" do
451
+ expect(workspace.directory("/").get_permissions.class).to eq Hash
452
+ end
453
+ end
454
+
455
+ context "オーナー変更のテスト" do
456
+ let :workspace_name do
457
+ "test_dir_workspace_chown"
458
+ end
459
+ before do
460
+ if !(dir = workspace.directory("chown")).exists?
461
+ dir.create
462
+ workspace.file("chown/hoge.txt").create
463
+ workspace.directory("chown_r").create
464
+ workspace.file("chown_r/fuga.txt").create
465
+ end
466
+ end
467
+ it "userは権限がないので、オーナー変更できない" do
468
+ expect do
469
+ workspace.directory("chown").chown(identifier: "hoge")
470
+ end.to raise_error
471
+ end
472
+ context "superuserはオーナー変更できる" do
473
+ it "recursive オプションが無いと、指定したディレクトリのみ変更される" do
474
+ superuser = StoreAgent::Superuser.new
475
+ superuser.workspace(workspace_name).directory("chown").chown(identifier: "hoge")
476
+ expect(workspace.directory("chown").metadata["owner"]).to eq "hoge"
477
+ expect(workspace.file("chown/hoge.txt").metadata["owner"]).to eq "foo"
478
+ end
479
+ it "recursive: true にすると、指定したディレクトリ以下の全ファイルが変更される" do
480
+ superuser = StoreAgent::Superuser.new
481
+ superuser.workspace(workspace_name).directory("chown_r").chown(identifier: "hoge", recursive: true)
482
+ expect(workspace.directory("chown_r").metadata["owner"]).to eq "hoge"
483
+ expect(workspace.file("chown_r/fuga.txt").metadata["owner"]).to eq "hoge"
484
+ end
485
+ end
486
+ end
487
+
488
+ context "配下オブジェクトを取得するテスト" do
489
+ let :workspace_name do
490
+ "test_dir_workspace_delete"
491
+ end
492
+ let :dir do
493
+ workspace.directory("search_children")
494
+ end
495
+ before do
496
+ if !dir.exists?
497
+ dir.create
498
+ workspace.file("search_children/foo.txt").create
499
+ workspace.directory("search_children/bar").create
500
+ workspace.directory("search_children/symlink_dir").create
501
+ end
502
+ end
503
+
504
+ context "ディレクトリを検索する" do
505
+ it "配下に存在するディレクトリを検索する" do
506
+ find_dir = dir.directory("bar")
507
+ expect(find_dir.path).to eq "/search_children/bar/"
508
+ expect(find_dir.exists?).to be true
509
+ end
510
+ it "配下に存在しないディレクトリを検索する" do
511
+ find_dir = dir.directory("foobar")
512
+ expect(find_dir.path).to eq "/search_children/foobar/"
513
+ expect(find_dir.exists?).to be false
514
+ end
515
+ it ". を検索すると、カレントディレクトリが返ってくる" do
516
+ expect(dir.directory(".").path).to eq "/search_children/"
517
+ end
518
+ it "/ を検索すると、カレントディレクトリが返ってくる" do
519
+ expect(dir.directory("/").path).to eq "/search_children/"
520
+ end
521
+ it ".. は / に変換されて検索される" do
522
+ expect(dir.directory("..").path).to eq "/search_children/"
523
+ end
524
+ end
525
+ context "ファイルを検索する" do
526
+ it "配下に存在するファイルを検索する" do
527
+ file = dir.file("foo.txt")
528
+ expect(file.path).to eq "/search_children/foo.txt"
529
+ expect(file.exists?).to be true
530
+ end
531
+ it "配下に存在しないファイルを検索する" do
532
+ file = dir.file("foobar.txt")
533
+ expect(file.path).to eq "/search_children/foobar.txt"
534
+ expect(file.exists?).to be false
535
+ end
536
+ it ".. は / に変換されて検索される" do
537
+ expect(dir.file("../foo.txt").path).to eq "/search_children/foo.txt"
538
+ end
539
+ end
540
+ context "タイプが不明なオブジェクトを検索する" do
541
+ it "パスがディレクトリなら、ディレクトリオブジェクトを返す" do
542
+ expect(dir.find_object("bar").class).to eq StoreAgent::Node::DirectoryObject
543
+ end
544
+ it "パスがファイルなら、ファイルオブジェクトを返す" do
545
+ expect(dir.find_object("foo.txt").class).to eq StoreAgent::Node::FileObject
546
+ end
547
+ it "パスに非対応形式のファイルが存在する場合、例外が発生する" do
548
+ File.symlink("/dev/null", "#{dir.storage_object_path}/symlink_dir/symlink")
549
+ expect do
550
+ dir.find_object("symlink_dir/symlink")
551
+ end.to raise_error
552
+ end
553
+ it "パスにファイルが存在しない場合、仮想オブジェクトを返す" do
554
+ expect(dir.find_object("foobar.txt").class).to eq StoreAgent::Node::VirtualObject
555
+ end
556
+ end
557
+ context "直下のファイル一覧を取得する" do
558
+ it "ディレクトリの直下にあるファイル一覧を返す" do
559
+ expect(dir.children.map(&:path).sort).to eq ["/search_children/bar/", "/search_children/foo.txt", "/search_children/symlink_dir/"].sort
560
+ end
561
+ end
562
+ end
563
+ end