Completed
Push — add/changelog-tooling ( 257a85 )
by
unknown
149:15 queued 138:48
created

AddCommandTest::testExecute_fileExists()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
/**
3
 * Tests for the changelogger add command.
4
 *
5
 * @package automattic/jetpack-changelogger
6
 */
7
8
// phpcs:disable WordPress.WP.AlternativeFunctions, WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
9
10
namespace Automattic\Jetpack\Changelogger\Tests\Console;
11
12
use Automattic\Jetpack\Changelogger\Utils;
13
use Symfony\Component\Console\Helper\DebugFormatterHelper;
14
use Symfony\Component\Console\Output\NullOutput;
15
use Wikimedia\TestingAccessWrapper;
16
17
/**
18
 * Tests for the changelogger add command.
19
 *
20
 * @covers \Automattic\Jetpack\Changelogger\Console\AddCommand
21
 */
22
class AddCommandTest extends CommandTestCase {
23
	use \Yoast\PHPUnitPolyfills\Polyfills\AssertionRenames;
24
25
	/**
26
	 * Set up.
27
	 *
28
	 * @before
29
	 */
30
	public function set_up() {
31
		parent::set_up();
32
		$this->useTempDir();
33
	}
34
35
	/**
36
	 * Test getDefaultFilename().
37
	 */
38
	public function testGetDefaultFilename() {
39
		$output = new NullOutput();
40
		$w      = TestingAccessWrapper::newFromObject( $this->getCommand( 'add' ) );
41
42
		// Test with no git checkout.
43
		$this->assertMatchesRegularExpression( '/^\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2}-\d{6}$/', $w->getDefaultFilename( $output ) );
44
45
		// Create a git checkout, master branch.
46
		$args = array( $output, new DebugFormatterHelper(), array( 'mustRun' => true ) );
47
		Utils::runCommand( array( 'git', 'init', '.' ), ...$args );
0 ignored issues
show
Bug introduced by
The call to runCommand() misses a required argument $formatter.

This check looks for function calls that miss required arguments.

Loading history...
Documentation introduced by
$args is of type array<integer,object<Sym...tRun\":\"boolean\"}>"}>, but the function expects a object<Symfony\Component...Output\OutputInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
48
		Utils::runCommand( array( 'git', 'checkout', '-b', 'master' ), ...$args );
0 ignored issues
show
Bug introduced by
The call to runCommand() misses a required argument $formatter.

This check looks for function calls that miss required arguments.

Loading history...
Documentation introduced by
$args is of type array<integer,object<Sym...tRun\":\"boolean\"}>"}>, but the function expects a object<Symfony\Component...Output\OutputInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
49
		Utils::runCommand( array( 'git', 'commit', '--author', 'Dummy <[email protected]>', '--allow-empty', '-m', 'Empty' ), ...$args );
0 ignored issues
show
Bug introduced by
The call to runCommand() misses a required argument $formatter.

This check looks for function calls that miss required arguments.

Loading history...
Documentation introduced by
$args is of type array<integer,object<Sym...tRun\":\"boolean\"}>"}>, but the function expects a object<Symfony\Component...Output\OutputInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
50
		$this->assertMatchesRegularExpression( '/^\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2}-\d{6}$/', $w->getDefaultFilename( $output ) );
51
52
		// Try a named branch.
53
		Utils::runCommand( array( 'git', 'checkout', '-b', 'test/default-filename' ), ...$args );
0 ignored issues
show
Bug introduced by
The call to runCommand() misses a required argument $formatter.

This check looks for function calls that miss required arguments.

Loading history...
Documentation introduced by
$args is of type array<integer,object<Sym...tRun\":\"boolean\"}>"}>, but the function expects a object<Symfony\Component...Output\OutputInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
54
		$this->assertSame( 'test-default-filename', $w->getDefaultFilename( $output ) );
55
	}
56
57
	/**
58
	 * Test mkdir failure in execute().
59
	 */
60
	public function testExecute_mkdirFail() {
61
		file_put_contents( 'changelog', '' );
62
		$tester = $this->getTester( 'add' );
63
		$code   = $tester->execute( array() );
64
		$this->assertSame( 1, $code );
65
		$this->assertMatchesRegularExpression( '{^Could not create directory /.*/phpunit-changelogger-[0-9a-f]{6}/changelog: mkdir\(\): File exists\n$}', $tester->getDisplay() );
66
	}
67
68
	/**
69
	 * Test failure in execute() when file exists.
70
	 */
71
	public function testExecute_fileExists() {
72
		mkdir( 'changelog' );
73
		file_put_contents( 'changelog/testing', '' );
74
		$tester = $this->getTester( 'add' );
75
		$code   = $tester->execute( array( '--filename' => 'testing' ), array( 'interactive' => false ) );
76
		$this->assertSame( 1, $code );
77
		$this->assertMatchesRegularExpression( '{^File "/.*/phpunit-changelogger-[0-9a-f]{6}/changelog/testing" already exists. If you want to replace it, delete it manually.\n$}', $tester->getDisplay() );
78
	}
79
80
	/**
81
	 * Test the command.
82
	 *
83
	 * @dataProvider provideExecute
84
	 * @param string[]    $args Command line arguments.
85
	 * @param array       $options Options for CommandTester.
86
	 * @param string[]    $inputs User inputs.
87
	 * @param int         $expectExitCode Expected exit code.
88
	 * @param string|null $expectFile Expected change file contents, or null if no file should exist.
89
	 * @param string[]    $expectOutputRegexes Regexes to run against the output.
90
	 */
91
	public function testExecute( array $args, array $options, array $inputs, $expectExitCode, $expectFile, $expectOutputRegexes = array() ) {
92
		$tester = $this->getTester( 'add' );
93
		$tester->setInputs( $inputs );
94
		$code = $tester->execute( $args, $options );
95
		foreach ( $expectOutputRegexes as $re ) {
96
			$this->assertMatchesRegularExpression( $re, $tester->getDisplay() );
97
		}
98
		$this->assertSame( $expectExitCode, $code );
99
		$this->assertDirectoryExists( './changelog' );
100
		$files = glob( './changelog/*' );
101
		if ( null === $expectFile ) {
102
			$this->assertCount( 0, $files, 'No change file is expected' );
103
		} else {
104
			$this->assertCount( 1, $files, 'A change file is expected' );
105
			$this->assertSame( $expectFile, file_get_contents( $files[0] ) );
106
		}
107
	}
108
109
	/**
110
	 * Data provider for testExecute.
111
	 */
112
	public function provideExecute() {
113
		return array(
114
			'Normal interactive use'                      => array(
115
				array(),
116
				array(),
117
				array( '', 'patch', 'fixed', '', 'Testing.' ),
118
				0,
119
				"Significance: patch\nType: fixed\n\nTesting.\n",
120
			),
121
			'Normal interactive use with comment'         => array(
122
				array(),
123
				array(),
124
				array( '', 'patch', 'fixed', 'This is a comment', 'Testing.' ),
125
				0,
126
				"Significance: patch\nType: fixed\nComment: This is a comment\n\nTesting.\n",
127
			),
128
			'Normal interactive use with empty entry'     => array(
129
				array(),
130
				array(),
131
				array( '', 'patch', 'fixed', '', '' ),
132
				0,
133
				"Significance: patch\nType: fixed\n\n\n",
134
			),
135
			'Interactive use with command line defaults'  => array(
136
				array(
137
					'--significance' => 'patch',
138
					'--type'         => 'fixed',
139
					'--entry'        => 'Testing.',
140
				),
141
				array(),
142
				array( '', '', '', '', '' ),
143
				0,
144
				"Significance: patch\nType: fixed\n\nTesting.\n",
145
			),
146
			'Interactive use that runs into some errors'  => array(
147
				array(),
148
				array(),
149
				array(
150
					'<bad filename>',
151
					'bad:filename?',
152
					'bad/|\\filename',
153
					'.bad-ilename',
154
					'',
155
					'extreme',
156
					'minor',
157
					'improved',
158
					'fixed',
159
					'',
160
					'',
161
					'Testing.',
162
				),
163
				0,
164
				"Significance: minor\nType: fixed\n\nTesting.\n",
165
				array(
166
					'/Filename may not contain angle brackets/',
167
					'/Filename may not contain colons or question marks/',
168
					'/Filename may not contain slashes, backslashes, or pipes/',
169
					'/Filename may not begin with a dot/',
170
					'/Value "extreme" is invalid/',
171
					'/Value "improved" is invalid/',
172
					'/An empty changelog entry is only allowed when the significance is "patch"/',
173
				),
174
			),
175
176
			'Normal non-interactive use'                  => array(
177
				array(
178
					'--significance' => 'patch',
179
					'--type'         => 'fixed',
180
					'--entry'        => 'Testing.',
181
				),
182
				array( 'interactive' => false ),
183
				array(),
184
				0,
185
				"Significance: patch\nType: fixed\n\nTesting.\n",
186
			),
187
			'Normal non-interactive use with comment'     => array(
188
				array(
189
					'--significance' => 'patch',
190
					'--type'         => 'fixed',
191
					'--comment'      => 'This is a comment',
192
					'--entry'        => 'Testing.',
193
				),
194
				array( 'interactive' => false ),
195
				array(),
196
				0,
197
				"Significance: patch\nType: fixed\nComment: This is a comment\n\nTesting.\n",
198
			),
199
			'Normal non-interactive use with empty entry' => array(
200
				array(
201
					'--significance' => 'patch',
202
					'--type'         => 'fixed',
203
					'--entry'        => '',
204
				),
205
				array( 'interactive' => false ),
206
				array(),
207
				0,
208
				"Significance: patch\nType: fixed\n\n\n",
209
			),
210
			'Non-interactive use with empty filename'     => array(
211
				array(
212
					'--filename'     => '',
213
					'--significance' => 'patch',
214
					'--type'         => 'fixed',
215
					'--entry'        => '',
216
				),
217
				array( 'interactive' => false ),
218
				array(),
219
				1,
220
				null,
221
				array( '/Filename may not be empty/' ),
222
			),
223
			'Non-interactive use with dot filename'       => array(
224
				array(
225
					'--filename'     => '.bad',
226
					'--significance' => 'patch',
227
					'--type'         => 'fixed',
228
					'--entry'        => '',
229
				),
230
				array( 'interactive' => false ),
231
				array(),
232
				1,
233
				null,
234
				array( '/Filename may not begin with a dot/' ),
235
			),
236
			'Non-interactive use with missing significance' => array(
237
				array(
238
					'--type'  => 'fixed',
239
					'--entry' => 'Testing.',
240
				),
241
				array( 'interactive' => false ),
242
				array(),
243
				1,
244
				null,
245
				array(
246
					'/Significance must be specified in non-interactive mode/',
247
				),
248
			),
249
			'Non-interactive use with invalid significance' => array(
250
				array(
251
					'--significance' => 'bogus',
252
					'--type'         => 'fixed',
253
					'--entry'        => 'Testing.',
254
				),
255
				array( 'interactive' => false ),
256
				array(),
257
				1,
258
				null,
259
				array(
260
					'/Significance value "bogus" is not valid/',
261
				),
262
			),
263
			'Non-interactive use with missing type'       => array(
264
				array(
265
					'--significance' => 'patch',
266
					'--entry'        => 'Testing.',
267
				),
268
				array( 'interactive' => false ),
269
				array(),
270
				1,
271
				null,
272
				array(
273
					'/Type must be specified in non-interactive mode/',
274
				),
275
			),
276
			'Non-interactive use with invalid type'       => array(
277
				array(
278
					'--significance' => 'patch',
279
					'--type'         => 'bogus',
280
					'--entry'        => 'Testing.',
281
				),
282
				array( 'interactive' => false ),
283
				array(),
284
				1,
285
				null,
286
				array(
287
					'/Type "bogus" is not valid/',
288
				),
289
			),
290
			'Non-interactive use with missing entry'      => array(
291
				array(
292
					'--significance' => 'patch',
293
					'--type'         => 'fixed',
294
				),
295
				array( 'interactive' => false ),
296
				array(),
297
				1,
298
				null,
299
				array(
300
					'/Entry must be specified in non-interactive mode/',
301
				),
302
			),
303
			'Non-interactive use with invalid entry'      => array(
304
				array(
305
					'--significance' => 'minor',
306
					'--type'         => 'fixed',
307
					'--entry'        => '',
308
				),
309
				array( 'interactive' => false ),
310
				array(),
311
				1,
312
				null,
313
				array(
314
					'/An empty changelog entry is only allowed when the significance is "patch"/',
315
				),
316
			),
317
		);
318
	}
319
320
}
321