rspec-path_matchers 0.1.1 → 0.2.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.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/.rubocop.yml +5 -0
- data/CHANGELOG.md +21 -0
- data/README.md +161 -260
- data/design.rb +10 -10
- data/lib/rspec/path_matchers/matchers/base.rb +210 -56
- data/lib/rspec/path_matchers/matchers/directory_matcher.rb +172 -0
- data/lib/rspec/path_matchers/matchers/{have_file.rb → file_matcher.rb} +8 -5
- data/lib/rspec/path_matchers/matchers/no_entry_matcher.rb +64 -0
- data/lib/rspec/path_matchers/matchers/{have_symlink.rb → symlink_matcher.rb} +9 -6
- data/lib/rspec/path_matchers/options/atime.rb +6 -40
- data/lib/rspec/path_matchers/options/base.rb +296 -0
- data/lib/rspec/path_matchers/options/birthtime.rb +6 -49
- data/lib/rspec/path_matchers/options/content.rb +13 -42
- data/lib/rspec/path_matchers/options/ctime.rb +6 -40
- data/lib/rspec/path_matchers/options/etc_base.rb +42 -0
- data/lib/rspec/path_matchers/options/file_stat_base.rb +47 -0
- data/lib/rspec/path_matchers/options/group.rb +5 -52
- data/lib/rspec/path_matchers/options/json_content.rb +6 -30
- data/lib/rspec/path_matchers/options/mode.rb +6 -40
- data/lib/rspec/path_matchers/options/mtime.rb +7 -41
- data/lib/rspec/path_matchers/options/owner.rb +6 -53
- data/lib/rspec/path_matchers/options/parsed_content_base.rb +67 -0
- data/lib/rspec/path_matchers/options/size.rb +5 -40
- data/lib/rspec/path_matchers/options/symlink_atime.rb +7 -40
- data/lib/rspec/path_matchers/options/symlink_birthtime.rb +7 -49
- data/lib/rspec/path_matchers/options/symlink_ctime.rb +7 -40
- data/lib/rspec/path_matchers/options/symlink_group.rb +6 -52
- data/lib/rspec/path_matchers/options/symlink_mtime.rb +7 -40
- data/lib/rspec/path_matchers/options/symlink_owner.rb +7 -53
- data/lib/rspec/path_matchers/options/symlink_target.rb +6 -43
- data/lib/rspec/path_matchers/options/symlink_target_exist.rb +6 -41
- data/lib/rspec/path_matchers/options/symlink_target_type.rb +20 -43
- data/lib/rspec/path_matchers/options/yaml_content.rb +6 -31
- data/lib/rspec/path_matchers/options.rb +1 -0
- data/lib/rspec/path_matchers/refinements.rb +79 -0
- data/lib/rspec/path_matchers/version.rb +1 -1
- data/lib/rspec/path_matchers.rb +185 -16
- metadata +12 -8
- data/lib/rspec/path_matchers/matchers/directory_contents_inspector.rb +0 -57
- data/lib/rspec/path_matchers/matchers/have_directory.rb +0 -126
- data/lib/rspec/path_matchers/matchers/have_no_entry.rb +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 485c656106b438d3aa53d5c4795e4d9a6e784e8e8e6aafa4b42aa704e5df9a36
|
4
|
+
data.tar.gz: '093a33b369d0a8d9c904080c84099f7f9cfe0f4095d2f3e5cba5b06af23ecd94'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef5013d34444c276cae1a37e18071780d1ab79045491222e022a6bd9391480dbf22d68abe8142dfb00036b06a2eb3fdb88823bc8b05029ce6f1bb78093a708b9
|
7
|
+
data.tar.gz: a28d711d8edc2494e5e228c23f2269f8936d01eb62566d3fe8a35f5a56ba66b3d64bc4e5c8e915dd22570f8f8ffcaa275de95a4a91dd3eb0953eecd09694bf54
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.2.0](https://github.com/main-branch/rspec-path_matchers/compare/v0.1.1...v0.2.0) (2025-06-30)
|
4
|
+
|
5
|
+
|
6
|
+
### ⚠ BREAKING CHANGES
|
7
|
+
|
8
|
+
* **dsl:** The format of failure messages has been completely redesigned. `be_dir`, `be_file`, and `be_symlink` are introduced as the preferred top-level matchers for clarity.
|
9
|
+
* You must use `#containing` to to set expectations on dir content instead of a block
|
10
|
+
|
11
|
+
### Features
|
12
|
+
|
13
|
+
* Add the be_dir matcher ([b16bf00](https://github.com/main-branch/rspec-path_matchers/commit/b16bf005fabba9dfa55b284543c1347efcaae62e))
|
14
|
+
* Change how expectations are nested for have_dir and be_dir ([e61d8fd](https://github.com/main-branch/rspec-path_matchers/commit/e61d8fddf0b6f3290fbdd01c15940b5a8a2e7361))
|
15
|
+
* **dsl:** Introduce be_* matchers for a clearer and more robust API ([0c76e85](https://github.com/main-branch/rspec-path_matchers/commit/0c76e8564215ae3e86b72135c7793c5ca386b53e))
|
16
|
+
|
17
|
+
|
18
|
+
### Other Changes
|
19
|
+
|
20
|
+
* Refactor the Options classes to reduce duplicaate code ([7e23190](https://github.com/main-branch/rspec-path_matchers/commit/7e231902a1a56162511a745d529c19acd06d50e6))
|
21
|
+
* Update README to give an example toward the top of the doc ([ece4afc](https://github.com/main-branch/rspec-path_matchers/commit/ece4afc3edb1e27e96fa3fe70052c99a73f6a221))
|
22
|
+
* Update README to give an example toward the top of the doc ([5b0f196](https://github.com/main-branch/rspec-path_matchers/commit/5b0f19646fe6cd7b7cba6c057e690b7b54d5c3c3))
|
23
|
+
|
3
24
|
## [0.1.1](https://github.com/main-branch/rspec-path_matchers/compare/v0.1.0...v0.1.1) (2025-06-25)
|
4
25
|
|
5
26
|
|
data/README.md
CHANGED
@@ -10,13 +10,13 @@ License](https://img.shields.io/badge/license-MIT-green)](https://opensource.org
|
|
10
10
|
- [Summary](#summary)
|
11
11
|
- [Installation](#installation)
|
12
12
|
- [Setup](#setup)
|
13
|
-
- [Usage
|
13
|
+
- [Usage](#usage)
|
14
14
|
- [Basic Assertions](#basic-assertions)
|
15
|
-
- [Negative Assertions (Checking for Absence)](#negative-assertions-checking-for-absence)
|
16
|
-
- [File Content Assertions](#file-content-assertions)
|
17
15
|
- [Attribute Assertions](#attribute-assertions)
|
18
|
-
- [Directory
|
19
|
-
- [
|
16
|
+
- [Directory Content \& Nested Assertions](#directory-content--nested-assertions)
|
17
|
+
- [Clear Failure Messages](#clear-failure-messages)
|
18
|
+
- [Available Options](#available-options)
|
19
|
+
- [Added Value Over Standard RSpec Matchers](#added-value-over-standard-rspec-matchers)
|
20
20
|
- [Development](#development)
|
21
21
|
- [Contributing](#contributing)
|
22
22
|
- [License](#license)
|
@@ -24,13 +24,143 @@ License](https://img.shields.io/badge/license-MIT-green)](https://opensource.org
|
|
24
24
|
## Summary
|
25
25
|
|
26
26
|
**RSpec::PathMatchers** provides a comprehensive suite of RSpec matchers for
|
27
|
-
testing
|
27
|
+
testing directory structures.
|
28
28
|
|
29
29
|
Verifying that a generator, build script, or any file-manipulating process has
|
30
30
|
produced the correct output can be tedious and verbose. This gem makes those
|
31
|
-
assertions simple, declarative, and
|
31
|
+
assertions simple, declarative, and easier to read, allowing you to describe an entire
|
32
32
|
file tree and its properties within your specs.
|
33
33
|
|
34
|
+
## Installation
|
35
|
+
|
36
|
+
Add this line to your application's `Gemfile` in the `:test` or `:development` group:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
group :test, :development do
|
40
|
+
gem 'rspec-path_matchers'
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
OR add it to your project's `gemspec`:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
spec.add_development_dependency 'rspec-path_matchers'
|
48
|
+
```
|
49
|
+
|
50
|
+
And then execute:
|
51
|
+
|
52
|
+
```bash
|
53
|
+
bundle install
|
54
|
+
```
|
55
|
+
|
56
|
+
## Setup
|
57
|
+
|
58
|
+
Require the gem in your `spec/spec_helper.rb` file:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
# spec/spec_helper.rb
|
62
|
+
require 'rspec/path_matchers'
|
63
|
+
|
64
|
+
RSpec.configure do |config|
|
65
|
+
config.include RSpec::PathMatchers
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
## Usage
|
70
|
+
|
71
|
+
### Basic Assertions
|
72
|
+
|
73
|
+
In their simplest forms, the `be_dir`, `be_file`, and `be_symlink` matchers **verify
|
74
|
+
the existence and type** of the given path. Each also supports negative expectations.
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
# Check for existence and type
|
78
|
+
expect('/tmp/new_project').to be_dir
|
79
|
+
|
80
|
+
# Check for absence
|
81
|
+
expect('/tmp/new_project').not_to be_file
|
82
|
+
```
|
83
|
+
|
84
|
+
### Attribute Assertions
|
85
|
+
|
86
|
+
All top-level and nested matchers can take options as a hash to assert on specific
|
87
|
+
file attributes. The value of each option can be an RSpec matcher or a literal value.
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
expect('config.yml').to be_file(content: include('production'), size: be < 1000)
|
91
|
+
|
92
|
+
expect('app.sock').to be_symlink(target: '/var/run/app.sock')
|
93
|
+
```
|
94
|
+
|
95
|
+
### Directory Content & Nested Assertions
|
96
|
+
|
97
|
+
The real power comes from describing the contents of a directory. The `#containing`
|
98
|
+
method asserts that a directory contains at least the specified entries, while
|
99
|
+
`#containing_exactly` asserts that it has exactly those entries and no others.
|
100
|
+
|
101
|
+
These expectations can be nested to any depth to describe a complete directory tree.
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
expect('/var/www').to(
|
105
|
+
be_dir.containing(
|
106
|
+
file('index.html', mode: '0644'),
|
107
|
+
dir('assets').containing_exactly(
|
108
|
+
file('app.js'),
|
109
|
+
file('style.css')
|
110
|
+
),
|
111
|
+
no_file_named('config.php') # Assert that an entry does not exist
|
112
|
+
)
|
113
|
+
)
|
114
|
+
```
|
115
|
+
|
116
|
+
Methods available as arguments to the containing methods include `dir`, `file`,
|
117
|
+
`symlink`, `no_file_named`, `no_dir_named`, and `no_symlink_named`.
|
118
|
+
|
119
|
+
### Clear Failure Messages
|
120
|
+
|
121
|
+
When an expectation is not met, this library gives well-formatted, easy-to-diagnose
|
122
|
+
error messages. If the index.html file from the previous example had the wrong
|
123
|
+
permissions AND style.css did not exist, the error would be:
|
124
|
+
|
125
|
+
```text
|
126
|
+
'/var/www' was not as expected:
|
127
|
+
- index.html
|
128
|
+
expected mode to be '0644', but it was '0600'
|
129
|
+
- assets/style.css
|
130
|
+
expected it to exist
|
131
|
+
```
|
132
|
+
|
133
|
+
### Available Options
|
134
|
+
|
135
|
+
Here is a list of all options that can be given to the matchers in this gem:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
mode: <matcher|String>,
|
139
|
+
size: <matcher|Integer>, # only for file matchers
|
140
|
+
|
141
|
+
# Owner and group require a Unix-like platform that supports the Etc module.
|
142
|
+
owner: <matcher|String>,
|
143
|
+
group: <matcher|String>,
|
144
|
+
|
145
|
+
# See `File.birthtime`, `File.atime`, etc. for platform support
|
146
|
+
birthtime: <matcher|Time|DateTime>,
|
147
|
+
atime: <matcher|Time|DateTime>,
|
148
|
+
ctime: <matcher|Time|DateTime>,
|
149
|
+
mtime: <matcher|Time|DateTime>,
|
150
|
+
|
151
|
+
# Content matchers are only for file matchers
|
152
|
+
content: <matcher|String|Regexp>,
|
153
|
+
json_content: <matcher|true>,
|
154
|
+
yaml_content: <matcher|true>
|
155
|
+
|
156
|
+
# Target matchers are only for symlink matchers
|
157
|
+
target: <matcher|String>
|
158
|
+
target_type: <matcher|String|Symbol> # e.g., 'file', 'directory'
|
159
|
+
target_exist: <matcher|true|false>
|
160
|
+
```
|
161
|
+
|
162
|
+
## Added Value Over Standard RSpec Matchers
|
163
|
+
|
34
164
|
Here’s a breakdown of the value this API provides over what is available in standard
|
35
165
|
RSpec.
|
36
166
|
|
@@ -55,7 +185,7 @@ With this API (Declarative Style):
|
|
55
185
|
```ruby
|
56
186
|
# This code describes the WHAT. The implementation details are hidden.
|
57
187
|
# It reads like a specification document.
|
58
|
-
expect('/var/www/html').to
|
188
|
+
expect('/var/www/html/index.html').to be_file(mode: '0644', owner: 'httpd')
|
59
189
|
```
|
60
190
|
|
61
191
|
This hides the complex, imperative logic inside the matcher and exposes a clean,
|
@@ -80,9 +210,9 @@ expect(JSON.parse(File.read(path))['status']).to eq('complete')
|
|
80
210
|
With this API:
|
81
211
|
|
82
212
|
```ruby
|
83
|
-
expect('/var/data').to
|
213
|
+
expect('/var/data/status.json').to be_file(
|
84
214
|
size: be > 0,
|
85
|
-
content:
|
215
|
+
content: not_to(match(/error/)),
|
86
216
|
json_content: include('status' => 'complete')
|
87
217
|
)
|
88
218
|
```
|
@@ -111,15 +241,17 @@ With this API:
|
|
111
241
|
|
112
242
|
```ruby
|
113
243
|
# This is a clear, hierarchical specification of the directory's contents.
|
114
|
-
expect('/etc/service').to(
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
244
|
+
expect('/etc/service/nginx').to(
|
245
|
+
be_dir.containing(
|
246
|
+
file('run'),
|
247
|
+
dir('log'),
|
248
|
+
no_file_named('down')
|
249
|
+
)
|
250
|
+
)
|
119
251
|
```
|
120
252
|
|
121
|
-
The nested
|
122
|
-
|
253
|
+
The nested `containing` matchers allows you to write tests that humans
|
254
|
+
can make sense of.
|
123
255
|
|
124
256
|
<h3>4. Descriptive and Intelligible Failure Messages</h3>
|
125
257
|
|
@@ -128,9 +260,9 @@ saving you valuable debugging time.
|
|
128
260
|
|
129
261
|
**Standard RSpec Failure:**
|
130
262
|
|
131
|
-
```
|
263
|
+
```text
|
132
264
|
expected: true
|
133
|
-
|
265
|
+
got: false
|
134
266
|
```
|
135
267
|
|
136
268
|
This kind of message forces you to manually inspect the directory structure to understand
|
@@ -141,252 +273,21 @@ what went wrong.
|
|
141
273
|
You get a detailed, hierarchical report that shows the full expectation and
|
142
274
|
clearly marks what failed.
|
143
275
|
|
144
|
-
|
145
|
-
the entry 'my-app' at '/tmp/d20250622-12345-abcdef' was expected to satisfy the following but did not:
|
146
|
-
- have directory "config" containing:
|
147
|
-
- have file "database.yml" with owner "db_user" and mode "0600"
|
148
|
-
- expected owner to be "db_user", but was "root"
|
149
|
-
- expected mode to be "0600", but was "0644"
|
150
|
-
```
|
151
|
-
|
152
|
-
## Installation
|
153
|
-
|
154
|
-
Add this line to your application's `Gemfile` in the `:test` or `:development` group:
|
155
|
-
|
156
|
-
```ruby
|
157
|
-
group :test, :development do
|
158
|
-
gem 'rspec-path_matchers'
|
159
|
-
end
|
160
|
-
```
|
161
|
-
|
162
|
-
And then execute:
|
163
|
-
|
164
|
-
```bash
|
165
|
-
$ bundle install
|
166
|
-
```
|
167
|
-
|
168
|
-
Or install it yourself as:
|
169
|
-
|
170
|
-
```bash
|
171
|
-
$ gem install rspec-path_matchers
|
172
|
-
```
|
173
|
-
|
174
|
-
## Setup
|
175
|
-
|
176
|
-
Require the gem in your `spec/spec_helper.rb` file:
|
276
|
+
For this expectation:
|
177
277
|
|
178
278
|
```ruby
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
## Usage & Examples
|
184
|
-
|
185
|
-
All matchers operate on a base path, making your tests clean and portable.
|
186
|
-
|
187
|
-
### Basic Assertions
|
188
|
-
|
189
|
-
At its simplest, you can check for the existence of files and directories.
|
190
|
-
|
191
|
-
```ruby
|
192
|
-
it "creates the basic structure" do
|
193
|
-
# Setup: Create some files and directories in our temp dir
|
194
|
-
FileUtils.mkdir_p(File.join(@tmpdir, "app/models"))
|
195
|
-
FileUtils.touch(File.join(@tmpdir, "config.yml"))
|
196
|
-
|
197
|
-
# Assertions
|
198
|
-
expect(@tmpdir).to have_file("config.yml")
|
199
|
-
expect(@tmpdir).to have_dir("app")
|
200
|
-
end
|
201
|
-
```
|
202
|
-
|
203
|
-
### Negative Assertions (Checking for Absence)
|
204
|
-
|
205
|
-
You can use `not_to` to ensure that a file, directory, or symlink of a specific
|
206
|
-
type does not exist.
|
207
|
-
|
208
|
-
- `not_to have_file` passes if the entry is a directory, a symlink, or non-existent.
|
209
|
-
- `not_to have_dir` passes if the entry is a file, a symlink, or non-existent.
|
210
|
-
- `not_to have_symlink` passes if the entry is a file, a directory, or non-existent.
|
211
|
-
|
212
|
-
**Important:** Negative matchers cannot be given options (`:mode`, `:content`, etc.)
|
213
|
-
or blocks.
|
214
|
-
|
215
|
-
```ruby
|
216
|
-
it "can check for the absence of entries" do
|
217
|
-
# Setup
|
218
|
-
Dir.mkdir(File.join(@tmpdir, "existing_dir"))
|
219
|
-
File.write(File.join(@tmpdir, "existing_file.txt"), "content")
|
220
|
-
|
221
|
-
# Assert that a path that doesn't exist fails all checks
|
222
|
-
expect(@tmpdir).not_to have_file("non_existent.txt")
|
223
|
-
expect(@tmpdir).not_to have_dir("non_existent_dir")
|
224
|
-
expect(@tmpdir).not_to have_symlink("non_existent_link")
|
225
|
-
|
226
|
-
# Assert that an existing directory is NOT a file or symlink
|
227
|
-
expect(@tmpdir).not_to have_file("existing_dir")
|
228
|
-
expect(@tmpdir).not_to have_symlink("existing_dir")
|
229
|
-
# expect(@tmpdir).not_to have_dir("existing_dir") # This would fail
|
230
|
-
|
231
|
-
# Assert that an existing file is NOT a directory or symlink
|
232
|
-
expect(@tmpdir).not_to have_dir("existing_file.txt")
|
233
|
-
expect(@tmpdir).not_to have_symlink("existing_file.txt")
|
234
|
-
# expect(@tmpdir).not_to have_file("existing_file.txt") # This would fail
|
235
|
-
end
|
236
|
-
```
|
237
|
-
|
238
|
-
### File Content Assertions
|
239
|
-
|
240
|
-
Go beyond existence and inspect what's inside a file.
|
241
|
-
|
242
|
-
```ruby
|
243
|
-
before do
|
244
|
-
File.write(File.join(@tmpdir, "app.log"), "INFO: User logged in\nWARN: Low disk space")
|
245
|
-
File.write(File.join(@tmpdir, "config.json"), '{"theme":"dark","version":2}')
|
246
|
-
FileUtils.touch(File.join(@tmpdir, "empty.file"))
|
247
|
-
end
|
248
|
-
|
249
|
-
it "validates file content" do
|
250
|
-
# Check for content with a string or regex
|
251
|
-
expect(@tmpdir).to have_file("app.log", content: "INFO: User logged in")
|
252
|
-
expect(@tmpdir).to have_file("app.log", content: /WARN:.*space/)
|
253
|
-
|
254
|
-
# Check for the absence of content
|
255
|
-
expect(@tmpdir).to have_file("app.log", content: not(/ERROR/))
|
256
|
-
|
257
|
-
# Check if a file is empty
|
258
|
-
expect(@tmpdir).to have_file("empty.file", size: 0)
|
259
|
-
|
260
|
-
# Check for valid JSON and match its structure
|
261
|
-
expect(@tmpdir).to have_file("config.json", json_content: {
|
262
|
-
"theme" => "dark",
|
263
|
-
"version" => an_instance_of(Integer)
|
264
|
-
})
|
265
|
-
end
|
266
|
-
```
|
267
|
-
|
268
|
-
### Attribute Assertions
|
269
|
-
|
270
|
-
Matcher options allow detailed expectations on files, directories, and symlinks. Here
|
271
|
-
are the options available on the three top level matchers:
|
272
|
-
|
273
|
-
```text
|
274
|
-
expect(path).to have_file(
|
275
|
-
name, mode:, owner:, group:, ctime:, mtime:, size:, content:, json_content:, yaml_content:,
|
276
|
-
)
|
277
|
-
|
278
|
-
expect(path).to have_dir(
|
279
|
-
name, mode:, owner:, group:, ctime:, mtime:, exact:
|
280
|
-
)
|
281
|
-
|
282
|
-
expect(path).to have_symlink(
|
283
|
-
name, mode:, owner:, group:, ctime:, mtime:, target:, target_type:, target_exist:
|
279
|
+
expect('config').to(
|
280
|
+
be_dir.containing(
|
281
|
+
file('database.xml', owner: 'db_user', mode: '0600')
|
282
|
+
)
|
284
283
|
)
|
285
284
|
```
|
286
285
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
script_path = File.join(@tmpdir, "deploy.sh")
|
293
|
-
File.write(script_path, "#!/bin/bash\n...")
|
294
|
-
FileUtils.chmod(0755, script_path)
|
295
|
-
|
296
|
-
key_path = File.join(@tmpdir, "secret.key")
|
297
|
-
File.write(key_path, "KEY_DATA")
|
298
|
-
FileUtils.chmod(0600, key_path)
|
299
|
-
|
300
|
-
FileUtils.ln_s("deploy.sh", File.join(@tmpdir, "latest_script"))
|
301
|
-
end
|
302
|
-
|
303
|
-
it "validates file attributes" do
|
304
|
-
# A single file can have many attributes checked at once
|
305
|
-
expect(@tmpdir).to have_file("deploy.sh", mode: "0755", size: be > 10)
|
306
|
-
|
307
|
-
# On Unix systems, you can check ownership
|
308
|
-
current_user = Etc.getlogin
|
309
|
-
expect(@tmpdir).to have_file("secret.key", owner: current_user, mode: "0600")
|
310
|
-
|
311
|
-
# Check symlinks and their targets
|
312
|
-
expect(@tmpdir).to have_symlink("latest_script", target: "deploy.sh")
|
313
|
-
end
|
314
|
-
```
|
315
|
-
|
316
|
-
### Directory Structure Assertions
|
317
|
-
|
318
|
-
The block syntax is the most powerful feature. It allows you to describe and verify
|
319
|
-
an entire file tree, including both the presence and *absence* of entries using
|
320
|
-
methods like `no_file`, `no_dir`, and `no_symlink`.
|
321
|
-
|
322
|
-
```ruby
|
323
|
-
before do
|
324
|
-
# Generate a complex directory structure
|
325
|
-
app_dir = File.join(@tmpdir, "my-app")
|
326
|
-
FileUtils.mkdir_p(File.join(app_dir, "bin"))
|
327
|
-
FileUtils.mkdir_p(File.join(app_dir, "config"))
|
328
|
-
FileUtils.mkdir_p(File.join(app_dir, "log"))
|
329
|
-
|
330
|
-
File.write(File.join(app_dir, "bin/run"), "#!/bin/bash")
|
331
|
-
FileUtils.chmod(0755, File.join(app_dir, "bin/run"))
|
332
|
-
|
333
|
-
File.write(File.join(app_dir, "config/database.yml"), "adapter: postgresql")
|
334
|
-
FileUtils.ln_s("database.yml", File.join(app_dir, "config/db.yml"))
|
335
|
-
end
|
336
|
-
|
337
|
-
it "validates a nested directory structure" do
|
338
|
-
# Note the parentheses around the matcher and its block
|
339
|
-
expect(@tmpdir).to(have_dir("my-app") do
|
340
|
-
# Assert on the 'bin' directory and its contents
|
341
|
-
dir "bin" do
|
342
|
-
file "run", mode: "0755", content: /bash/
|
343
|
-
end
|
344
|
-
|
345
|
-
# Assert on the 'config' directory and its contents
|
346
|
-
dir "config" do
|
347
|
-
file "database.yml"
|
348
|
-
symlink "db.yml", target: "database.yml"
|
349
|
-
no_file "secrets.yml" # Assert that a file is NOT present
|
350
|
-
end
|
351
|
-
|
352
|
-
# Assert that the 'log' directory is present and empty
|
353
|
-
dir "log"
|
354
|
-
|
355
|
-
# Assert the absence of other entries at the root of 'my-app'
|
356
|
-
no_dir "tmp"
|
357
|
-
no_file "README.md"
|
358
|
-
end)
|
359
|
-
end
|
360
|
-
```
|
361
|
-
|
362
|
-
### Exact Directory Contents
|
363
|
-
|
364
|
-
You can enforce that a directory contains *only* the entries defined in your
|
365
|
-
specification block by using the `exact: true` option. This is perfect for testing
|
366
|
-
generators or build scripts that should produce a clean, specific output without any
|
367
|
-
extra files.
|
368
|
-
|
369
|
-
If any undeclared entries are found on the PathMatchers, the matcher will fail.
|
370
|
-
|
371
|
-
```ruby
|
372
|
-
it "creates a directory with only the expected files" do
|
373
|
-
# Setup: Create a directory with an extra, unexpected file.
|
374
|
-
FileUtils.mkdir(File.join(@tmpdir, 'dist'))
|
375
|
-
File.write(File.join(@tmpdir, 'dist/app.js'), '// ...')
|
376
|
-
File.write(File.join(@tmpdir, 'dist/unexpected.log'), 'debug info')
|
377
|
-
|
378
|
-
# This test will fail because 'unexpected.log' was not declared.
|
379
|
-
expect(@tmpdir).to(
|
380
|
-
have_dir('dist', exact: true) do
|
381
|
-
file 'app.js'
|
382
|
-
end
|
383
|
-
)
|
384
|
-
end
|
385
|
-
|
386
|
-
# Failure Message:
|
387
|
-
#
|
388
|
-
# the entry 'dist' at '...' was expected to satisfy the following but did not:
|
389
|
-
# - did not expect entries ["unexpected.log"] to be present
|
286
|
+
```text
|
287
|
+
'config' was not as expected:
|
288
|
+
- database.xml
|
289
|
+
expected owner to be "db_user", but was "root"
|
290
|
+
expected mode to be "0600", but was "0644"
|
390
291
|
```
|
391
292
|
|
392
293
|
## Development
|
data/design.rb
CHANGED
@@ -47,17 +47,17 @@ expect(path).to have_dir(name, mtime: matcher) # ...the directory modification t
|
|
47
47
|
|
48
48
|
# Nested directory checks ()
|
49
49
|
expect(path).to(
|
50
|
-
have_dir(name)
|
51
|
-
file('nested_file.txt', content: 'expected content')
|
52
|
-
|
50
|
+
have_dir(name).containing(
|
51
|
+
file('nested_file.txt', content: 'expected content'),
|
52
|
+
symlink('nested_symlink', target: 'expected_target'),
|
53
|
+
no_file_named('non_existent_file.txt'),
|
54
|
+
no_dir_named('non_existent_dir'),
|
55
|
+
no_symlink_named('non_existent_symlink'),
|
56
|
+
no_entry_named('non_existent_entry'),
|
57
|
+
dir('nested_dir').containing_exactly(
|
53
58
|
file('deeply_nested_file.txt', content: 'deeply expected content')
|
54
|
-
|
55
|
-
|
56
|
-
no_file('non_existent_file.txt')
|
57
|
-
no_dir('non_existent_dir')
|
58
|
-
no_symlink('non_existent_symlink')
|
59
|
-
no_entry('non_existent_entry') # This checks for the absence of any type of entry with the given name
|
60
|
-
end
|
59
|
+
)
|
60
|
+
)
|
61
61
|
)
|
62
62
|
|
63
63
|
########################################
|