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,339 @@
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::Metadata do
20
+ let :super_user do
21
+ StoreAgent::Superuser.new
22
+ end
23
+ let :owner do
24
+ StoreAgent::User.new("group", "owner")
25
+ end
26
+ let :namespaced_user do
27
+ StoreAgent::User.new(["user_id", "namespaced_id"])
28
+ end
29
+ let :workspace do
30
+ owner.workspace(workspace_name)
31
+ end
32
+
33
+ context "Workspace 作成直後のメタデータ" do
34
+ let :workspace_name do
35
+ "test_workspace_01"
36
+ end
37
+ before do
38
+ workspace.create if !workspace.exists?
39
+ @root_node = workspace.directory("/")
40
+ end
41
+
42
+ it "JSON形式で保存される" do
43
+ expect(open(@root_node.metadata.file_path).read).to eq @root_node.metadata.inspect
44
+ end
45
+ it "ルートディレクトリの使用容量は 4096 バイト" do
46
+ expect(@root_node.metadata.disk_usage).to eq 4096
47
+ end
48
+ end
49
+
50
+ context "ID が配列のユーザーで Workspace を作成した場合" do
51
+ it "owner は配列の先頭要素が適用される" do
52
+ namespaced_workspace = namespaced_user.workspace("test_workspace_namespaced_id")
53
+ namespaced_workspace.create
54
+ root_node = namespaced_workspace.root
55
+ expect(root_node.metadata["owner"]).to eq "user_id"
56
+ end
57
+ end
58
+
59
+ context "Workspace にディレクトリを作成した場合のメタデータ" do
60
+ let :workspace_name do
61
+ "test_workspace_02"
62
+ end
63
+ before do
64
+ if !workspace.exists?
65
+ workspace.create
66
+ owner.workspace(workspace_name).directory("foo").create
67
+ owner.workspace(workspace_name).directory("foo/bar").create
68
+ end
69
+ @root_node = workspace.directory("/")
70
+ @dir_1 = owner.workspace(workspace_name).directory("foo")
71
+ @dir_2 = owner.workspace(workspace_name).directory("foo/bar")
72
+ end
73
+
74
+ context "ルートディレクトリ" do
75
+ it "ディスク使用量は (4096 * 3) バイト" do
76
+ expect(@root_node.metadata.disk_usage).to eq (4096 * 3)
77
+ end
78
+ it "直下のファイル数は 1" do
79
+ expect(@root_node.directory_file_count).to eq 1
80
+ end
81
+ it "サブツリー全体の配下ファイル数は 2" do
82
+ expect(@root_node.tree_file_count).to eq 2
83
+ end
84
+ end
85
+ context "中間ディレクトリ" do
86
+ it "ディスク使用量は (4096 * 2) バイト" do
87
+ expect(@dir_1.metadata.disk_usage).to eq (4096 * 2)
88
+ end
89
+ it "直下のファイル数は 1" do
90
+ expect(@dir_1.directory_file_count).to eq 1
91
+ end
92
+ it "サブツリー全体の配下ファイル数は 1" do
93
+ expect(@dir_1.tree_file_count).to eq 1
94
+ end
95
+ end
96
+ context "最下層のディレクトリ" do
97
+ it "ディスク使用量は 4096 バイト" do
98
+ expect(@dir_2.metadata.disk_usage).to eq 4096
99
+ end
100
+ it "直下のファイル数は 0" do
101
+ expect(@dir_2.directory_file_count).to eq 0
102
+ end
103
+ it "サブツリー全体の配下ファイル数は 0" do
104
+ expect(@dir_2.tree_file_count).to eq 0
105
+ end
106
+ end
107
+ end
108
+
109
+ context "Workspace にディレクトリとファイルを作成した場合のメタデータ" do
110
+ let :workspace_name do
111
+ "test_workspace_03"
112
+ end
113
+ before do
114
+ if !workspace.exists?
115
+ workspace.create
116
+ owner.workspace(workspace_name).directory("foo").create
117
+ owner.workspace(workspace_name).file("foo/bar.txt").create("body" => "1234567890")
118
+ end
119
+ @root_node = workspace.directory("/")
120
+ @dir = owner.workspace(workspace_name).directory("foo")
121
+ @file = owner.workspace(workspace_name).file("foo/bar.txt")
122
+ end
123
+
124
+ context "ルートディレクトリ" do
125
+ it "ディスク使用量は (4096 * 2) + 10 バイト" do
126
+ expect(@root_node.metadata.disk_usage).to eq ((4096 * 2) + 10)
127
+ end
128
+ it "直下のファイル数は 1" do
129
+ expect(@root_node.directory_file_count).to eq 1
130
+ end
131
+ it "サブツリー全体の配下ファイル数は 2" do
132
+ expect(@root_node.tree_file_count).to eq 2
133
+ end
134
+ end
135
+ context "ディレクトリ" do
136
+ it "ディスク使用量は 4096 + 10 バイト" do
137
+ expect(@dir.metadata.disk_usage).to eq (4096 + 10)
138
+ end
139
+ it "直下のファイル数は 1" do
140
+ expect(@dir.directory_file_count).to eq 1
141
+ end
142
+ it "サブツリー全体の配下ファイル数は 1" do
143
+ expect(@dir.tree_file_count).to eq 1
144
+ end
145
+ end
146
+ context "ファイル" do
147
+ it "ディスク使用量は 10 バイト" do
148
+ expect(@file.metadata.disk_usage).to eq 10
149
+ end
150
+ it "ディレクトリではないので、配下のファイルは存在しない" do
151
+ expect do
152
+ @file.directory_file_count
153
+ end.to raise_error
154
+ expect do
155
+ @file.tree_file_count
156
+ end.to raise_error
157
+ end
158
+ end
159
+ end
160
+
161
+ context "ファイルの中身を更新した場合のメタデータ" do
162
+ let :workspace_name do
163
+ "test_workspace_04"
164
+ end
165
+ before do
166
+ if !workspace.exists?
167
+ workspace.create
168
+ owner.workspace(workspace_name).directory("foo").create
169
+ owner.workspace(workspace_name).file("foo/bar.txt").create("body" => "1234567890")
170
+ owner.workspace(workspace_name).file("foo/bar.txt").update("body" => "updated")
171
+ end
172
+ @root_node = workspace.directory("/")
173
+ @dir = owner.workspace(workspace_name).directory("foo")
174
+ @file = owner.workspace(workspace_name).file("foo/bar.txt")
175
+ end
176
+
177
+ context "ルートディレクトリ" do
178
+ it "ディスク使用量は (4096 * 2) + 7 バイト" do
179
+ expect(@root_node.metadata.disk_usage).to eq ((4096 * 2) + 7)
180
+ end
181
+ it "直下のファイル数は 1" do
182
+ expect(@root_node.directory_file_count).to eq 1
183
+ end
184
+ it "サブツリー全体の配下ファイル数は 2" do
185
+ expect(@root_node.tree_file_count).to eq 2
186
+ end
187
+ end
188
+ context "ディレクトリ" do
189
+ it "ディスク使用量は 4096 + 7 バイト" do
190
+ expect(@dir.metadata.disk_usage).to eq (4096 + 7)
191
+ end
192
+ it "直下のファイル数は 1" do
193
+ expect(@dir.directory_file_count).to eq 1
194
+ end
195
+ it "サブツリー全体の配下ファイル数は 1" do
196
+ expect(@dir.tree_file_count).to eq 1
197
+ end
198
+ end
199
+ context "ファイル" do
200
+ it "ディスク使用量は 7 バイト" do
201
+ expect(@file.metadata.disk_usage).to eq 7
202
+ end
203
+ it "ディレクトリではないので、配下のファイルは存在しない" do
204
+ expect do
205
+ @file.directory_file_count
206
+ end.to raise_error
207
+ expect do
208
+ @file.tree_file_count
209
+ end.to raise_error
210
+ end
211
+ end
212
+ end
213
+
214
+ context "ファイルを削除した場合のメタデータ" do
215
+ let :workspace_name do
216
+ "test_workspace_05"
217
+ end
218
+ before do
219
+ if !workspace.exists?
220
+ workspace.create
221
+ owner.workspace(workspace_name).directory("foo").create
222
+ owner.workspace(workspace_name).file("foo/bar.txt").create("body" => "1234567890")
223
+ owner.workspace(workspace_name).file("foo/foobar.txt").create("body" => "0987654321")
224
+ owner.workspace(workspace_name).file("foo/foobar.txt").delete
225
+ end
226
+ @root_node = workspace.directory("/")
227
+ @dir = owner.workspace(workspace_name).directory("foo")
228
+ end
229
+
230
+ context "ルートディレクトリ" do
231
+ it "ディスク使用量は (4096 * 2) + 10 バイト" do
232
+ expect(@root_node.metadata.disk_usage).to eq ((4096 * 2) + 10)
233
+ end
234
+ it "直下のファイル数は 1" do
235
+ expect(@root_node.directory_file_count).to eq 1
236
+ end
237
+ it "サブツリー全体の配下ファイル数は 2" do
238
+ expect(@root_node.tree_file_count).to eq 2
239
+ end
240
+ end
241
+ context "ディレクトリ" do
242
+ it "ディスク使用量は 4096 + 10 バイト" do
243
+ expect(@dir.metadata.disk_usage).to eq (4096 + 10)
244
+ end
245
+ it "直下のファイル数は 1" do
246
+ expect(@dir.directory_file_count).to eq 1
247
+ end
248
+ it "サブツリー全体の配下ファイル数は 1" do
249
+ expect(@dir.tree_file_count).to eq 1
250
+ end
251
+ it "ファイルの中身を変更後に保存しないで削除した場合、変更前の情報が使用される" do
252
+ file = owner.workspace(workspace_name).file("foo/foobarhoge.txt").create("body" => "0987654321")
253
+ file.body = ""
254
+ file.delete
255
+ expect(@dir.metadata.disk_usage).to eq (4096 + 10)
256
+ end
257
+ end
258
+ end
259
+
260
+ context "空ディレクトリを削除した場合のメタデータ" do
261
+ let :workspace_name do
262
+ "test_workspace_06"
263
+ end
264
+ before do
265
+ if !workspace.exists?
266
+ workspace.create
267
+ owner.workspace(workspace_name).directory("foo").create
268
+ owner.workspace(workspace_name).directory("foo/bar").create
269
+ owner.workspace(workspace_name).directory("foo/bar").delete
270
+ end
271
+ @root_node = workspace.directory("/")
272
+ @dir = owner.workspace(workspace_name).directory("foo")
273
+ end
274
+
275
+ context "ルートディレクトリ" do
276
+ it "ディスク使用量は 4096 * 2 バイト" do
277
+ expect(@root_node.metadata.disk_usage).to eq (4096 * 2)
278
+ end
279
+ it "直下のファイル数は 1" do
280
+ expect(@root_node.directory_file_count).to eq 1
281
+ end
282
+ it "サブツリー全体の配下ファイル数は 1" do
283
+ expect(@root_node.tree_file_count).to eq 1
284
+ end
285
+ end
286
+ context "ディレクトリ" do
287
+ it "ディスク使用量は 4096 バイト" do
288
+ expect(@dir.metadata.disk_usage).to eq 4096
289
+ end
290
+ it "直下のファイル数は 0" do
291
+ expect(@dir.directory_file_count).to eq 0
292
+ end
293
+ it "サブツリー全体の配下ファイル数は 0" do
294
+ expect(@dir.tree_file_count).to eq 0
295
+ end
296
+ end
297
+ end
298
+
299
+ context "中身があるディレクトリを削除した場合のメタデータ" do
300
+ let :workspace_name do
301
+ "test_workspace_07"
302
+ end
303
+ before do
304
+ if !workspace.exists?
305
+ workspace.create
306
+ owner.workspace(workspace_name).directory("foo").create
307
+ owner.workspace(workspace_name).directory("foo/bar").create
308
+ owner.workspace(workspace_name).file("foo/bar/hoge.txt").create("1234567890")
309
+ owner.workspace(workspace_name).file("foo/bar/fuga.txt").create("12345")
310
+ owner.workspace(workspace_name).directory("foo/bar").delete
311
+ end
312
+ @root_node = workspace.directory("/")
313
+ @dir = owner.workspace(workspace_name).directory("foo")
314
+ end
315
+
316
+ context "ルートディレクトリ" do
317
+ it "ディスク使用量は 4096 * 2 バイト" do
318
+ expect(@root_node.metadata.disk_usage).to eq (4096 * 2)
319
+ end
320
+ it "直下のファイル数は 1" do
321
+ expect(@root_node.directory_file_count).to eq 1
322
+ end
323
+ it "サブツリー全体の配下ファイル数は 1" do
324
+ expect(@root_node.tree_file_count).to eq 1
325
+ end
326
+ end
327
+ context "ディレクトリ" do
328
+ it "ディスク使用量は 4096 バイト" do
329
+ expect(@dir.metadata.disk_usage).to eq 4096
330
+ end
331
+ it "直下のファイル数は 0" do
332
+ expect(@dir.directory_file_count).to eq 0
333
+ end
334
+ it "サブツリー全体の配下ファイル数は 0" do
335
+ expect(@dir.tree_file_count).to eq 0
336
+ end
337
+ end
338
+ end
339
+ end
@@ -0,0 +1,73 @@
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::Object do
20
+ let :user do
21
+ StoreAgent::User.new("foo", "bar")
22
+ end
23
+ let :workspace do
24
+ user.workspace("bar")
25
+ end
26
+
27
+ context "初期化のテスト" do
28
+ it "Workspace が無い Object は作成できない" do
29
+ expect do
30
+ StoreAgent::Node::Object.new(path: "/")
31
+ end.to raise_error
32
+ end
33
+ end
34
+
35
+ context "初期化時に path の先頭の . や / を / に変換する" do
36
+ let :object do
37
+ StoreAgent::Node::Object.new(workspace: workspace, path: @path)
38
+ end
39
+
40
+ it "先頭に / が無ければ追加する" do
41
+ @path = "foo"
42
+ expect(object.path).to eq "/foo"
43
+ end
44
+ it ". は / に変換される" do
45
+ @path = "."
46
+ expect(object.path).to eq "/"
47
+ end
48
+ it "/// は / に変換される" do
49
+ @path = "///"
50
+ expect(object.path).to eq "/"
51
+ end
52
+ it "/././. は / に変換される" do
53
+ @path = "/././."
54
+ expect(object.path).to eq "/"
55
+ end
56
+ it "../.. は / に変換される" do
57
+ @path = "../.."
58
+ expect(object.path).to eq "/"
59
+ end
60
+ it "../foo/bar は /foo/bar に変換される" do
61
+ @path = "../foo/bar"
62
+ expect(object.path).to eq "/foo/bar"
63
+ end
64
+ end
65
+
66
+ context "オブジェクト作成" do
67
+ it "StoreAgent::Node::Object クラスそのままでは作成できない" do
68
+ expect do
69
+ StoreAgent::Node::Object.new(workspace: workspace, path: "/hoge.txt").create
70
+ end.to raise_error
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,121 @@
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::PathValidator do
20
+ let :user do
21
+ StoreAgent::User.new("foo")
22
+ end
23
+ let :workspace do
24
+ user.workspace("test_path_validator")
25
+ end
26
+ before :all do
27
+ workspace = StoreAgent::User.new("foo").workspace("test_path_validator")
28
+ workspace.create
29
+ workspace.directory("create").create
30
+ workspace.file("create/tmp.txt").create
31
+ workspace.directory("read").create
32
+ workspace.file("read/foo.txt").create("foo")
33
+ workspace.directory("update").create
34
+ workspace.file("update/bar.txt").create{|f| f.body = "bar"}
35
+ workspace.directory("delete").create
36
+ workspace.directory("delete/hoge/").create
37
+ workspace.file("delete/fuga.txt").create(:fuga)
38
+ end
39
+
40
+ context "create時のオブジェクト存在チェック" do
41
+ it "既にオブジェクトが存在する場合、ディレクトリ作成はエラーになる" do
42
+ expect do
43
+ workspace.directory("/create").create
44
+ end.to raise_error(StoreAgent::InvalidPathError)
45
+ expect do
46
+ workspace.directory("/create/tmp.txt").create
47
+ end.to raise_error
48
+ end
49
+ it "既にオブジェクトが存在する場合、ファイル作成はエラーになる" do
50
+ expect do
51
+ workspace.file("/create").create
52
+ end.to raise_error
53
+ expect do
54
+ workspace.file("/create/tmp.txt").create
55
+ end.to raise_error
56
+ end
57
+ it "オブジェクトが存在しなければ、ディレクトリ作成できる" do
58
+ workspace.directory("/create/new_dir").create
59
+ expect(workspace.directory("/create/new_dir").exists?).to be true
60
+ end
61
+ it "オブジェクトが存在しなければ、ファイル作成できる" do
62
+ workspace.file("/create/new_file.txt").create
63
+ expect(workspace.file("/create/new_file.txt").exists?).to be true
64
+ end
65
+ end
66
+ context "read時のオブジェクト存在チェック" do
67
+ it "存在しないオブジェクトの読み込みはエラーになる" do
68
+ expect do
69
+ workspace.directory("/read/foo/bar").read
70
+ end.to raise_error
71
+ expect do
72
+ workspace.file("/read/foo.bar.txt").read
73
+ end.to raise_error
74
+ end
75
+ it "存在するディレクトリは読み込める" do
76
+ expect do
77
+ workspace.directory("read").read
78
+ end.to_not raise_error
79
+ end
80
+ it "存在するファイルは読み込める" do
81
+ expect(workspace.file("read/foo.txt").read).to eq "foo"
82
+ end
83
+ end
84
+ context "update時のオブジェクト存在チェック" do
85
+ it "ディレクトリの更新はエラーになる" do
86
+ expect do
87
+ workspace.directory("/update").update
88
+ end.to raise_error
89
+ expect do
90
+ workspace.directory("/update/foo/bar").update
91
+ end.to raise_error
92
+ end
93
+ it "存在するファイルは更新できる" do
94
+ workspace.file("update/bar.txt").update("body")
95
+ expect(workspace.file("update/bar.txt").read).to eq "body"
96
+ end
97
+ it "存在しないファイルの更新はエラーになる" do
98
+ expect do
99
+ workspace.file("/update/foo.bar.txt").update("foobar")
100
+ end.to raise_error
101
+ end
102
+ end
103
+ context "delete時のオブジェクト存在チェック" do
104
+ it "存在しないオブジェクトを削除しようとするとエラーになる" do
105
+ expect do
106
+ workspace.directory("/delete/foo/bar").delete
107
+ end.to raise_error
108
+ expect do
109
+ workspace.file("/delete/foo/bar.txt").delete
110
+ end.to raise_error
111
+ end
112
+ it "ディレクトリが存在すれば削除できる" do
113
+ workspace.directory("delete/hoge/").delete
114
+ expect(workspace.directory("delete/hoge/").exists?).to be false
115
+ end
116
+ it "ファイルが存在すれば削除できる" do
117
+ workspace.file("delete/fuga.txt").delete
118
+ expect(workspace.file("delete/fuga.txt").exists?).to be false
119
+ end
120
+ end
121
+ end