1<?php
2
3namespace Wikimedia\ParamValidator\TypeDef;
4
5use InvalidArgumentException;
6use Wikimedia\Message\DataMessageValue;
7use Wikimedia\ParamValidator\SimpleCallbacks;
8use Wikimedia\ParamValidator\ValidationException;
9use Wikimedia\Timestamp\ConvertibleTimestamp;
10
11/**
12 * @covers \Wikimedia\ParamValidator\TypeDef\ExpiryDef
13 */
14class ExpiryDefTest extends TypeDefTestCase {
15
16	protected function getInstance( SimpleCallbacks $callbacks, array $options ) {
17		return new ExpiryDef( $callbacks, $options );
18	}
19
20	/**
21	 * Get an entry for the provideValidate() provider, where a given value
22	 * is asserted to cause a ValidationException with the given message.
23	 * @param string $value
24	 * @param string $msg
25	 * @param array $settings
26	 * @return array
27	 */
28	private function getValidationAssertion( string $value, string $msg, array $settings = [] ) {
29		return [
30			$value,
31			new ValidationException(
32				DataMessageValue::new( $msg ),
33				'expiry',
34				$value,
35				[]
36			),
37			$settings
38		];
39	}
40
41	/**
42	 * @dataProvider provideValidate
43	 */
44	public function testValidate(
45		$value, $expect, array $settings = [], array $options = [], array $expectConds = []
46	) {
47		ConvertibleTimestamp::setFakeTime( 1559764242 );
48		parent::testValidate( $value, $expect, $settings, $options, $expectConds );
49	}
50
51	public function provideValidate() {
52		$settings = [
53			ExpiryDef::PARAM_MAX => '6 months',
54			ExpiryDef::PARAM_USE_MAX => true,
55		];
56
57		return [
58			'Valid infinity' => [ 'indefinite', 'infinity' ],
59			'Invalid expiry' => $this->getValidationAssertion( 'foobar', 'badexpiry' ),
60			'Expiry in past' => $this->getValidationAssertion( '20150123T12:34:56Z', 'badexpiry-past' ),
61			'Expiry in past with unix 0' => $this->getValidationAssertion(
62				'1970-01-01T00:00:00Z',
63				'badexpiry-past'
64			),
65			'Expiry in past with negative unix time' => $this->getValidationAssertion(
66				'1969-12-31T23:59:59Z',
67				'badexpiry-past',
68				$settings
69			),
70			'Valid expiry' => [
71				'99990123123456',
72				'9999-01-23T12:34:56Z'
73			],
74			'Valid relative expiry' => [
75				'1 month',
76				'2019-07-05T19:50:42Z'
77			],
78			'Expiry less than max' => [ '20190701123456', '2019-07-01T12:34:56Z', $settings ],
79			'Relative expiry less than max' => [ '1 day', '2019-06-06T19:50:42Z', $settings ],
80			'Infinity less than max' => [ 'indefinite', 'infinity', $settings ],
81			'Expiry exceeds max' => [
82				'9999-01-23T12:34:56Z',
83				'2019-12-05T19:50:42Z',
84				$settings,
85				[],
86				[
87					[
88						'code' => 'paramvalidator-badexpiry-duration-max',
89						'data' => null,
90					]
91				],
92			],
93			'Relative expiry exceeds max' => [
94				'10 years',
95				'2019-12-05T19:50:42Z',
96				$settings,
97				[],
98				[
99					[
100						'code' => 'paramvalidator-badexpiry-duration-max',
101						'data' => null,
102					]
103				],
104			],
105			'Expiry exceeds max, fatal' => $this->getValidationAssertion(
106				'9999-01-23T12:34:56Z',
107				'paramvalidator-badexpiry-duration',
108				[
109					ExpiryDef::PARAM_MAX => '6 months',
110				]
111			),
112		];
113	}
114
115	public function testNormalizeExpiry() {
116		$this->assertNull( ExpiryDef::normalizeExpiry( null ) );
117		$this->assertSame(
118			'infinity',
119			ExpiryDef::normalizeExpiry( 'indefinite' )
120		);
121		$this->assertSame(
122			'2050-01-01T00:00:00Z',
123			ExpiryDef::normalizeExpiry( '205001010000', TS_ISO_8601 )
124		);
125		$this->assertSame(
126			'1970-01-01T00:00:00Z',
127			ExpiryDef::normalizeExpiry( '1970-01-01T00:00:00Z', TS_ISO_8601 )
128		);
129		$this->expectException( InvalidArgumentException::class );
130		$this->expectExceptionMessage( 'Invalid expiry value: 0' );
131		ExpiryDef::normalizeExpiry( 0, TS_ISO_8601 );
132	}
133
134	public function provideGetInfo() {
135		return [
136			'Basic' => [
137				[],
138				[],
139				[
140					// phpcs:ignore Generic.Files.LineLength.TooLong
141					'param-type' => '<message key="paramvalidator-help-type-expiry"><text>1</text><list listType="text"><text>&quot;infinite&quot;</text><text>&quot;indefinite&quot;</text><text>&quot;infinity&quot;</text><text>&quot;never&quot;</text></list></message>'
142				]
143			]
144		];
145	}
146
147	/**
148	 * @covers \Wikimedia\ParamValidator\TypeDef\ExpiryDef::normalizeUsingMaxExpiry
149	 */
150	public function testNormalizeUsingMaxExpiry() {
151		// Fake current time to be 2020-05-27T00:00:00Z
152		$fakeTime = ConvertibleTimestamp::setFakeTime( '20200527000000' );
153		$this->assertSame(
154			'2020-11-27T00:00:00Z',
155			ExpiryDef::normalizeUsingMaxExpiry( '10 months', '6 months', TS_ISO_8601 )
156		);
157		$this->assertSame(
158			'2020-10-27T00:00:00Z',
159			ExpiryDef::normalizeUsingMaxExpiry( '2020-10-27T00:00:00Z', '6 months', TS_ISO_8601 )
160		);
161		$this->assertSame(
162			'infinity',
163			ExpiryDef::normalizeUsingMaxExpiry( 'infinity', '6 months', TS_ISO_8601 )
164		);
165		$this->assertNull( ExpiryDef::normalizeUsingMaxExpiry( null, '6 months', TS_ISO_8601 ) );
166
167		$this->expectException( InvalidArgumentException::class );
168		$this->expectExceptionMessage( 'Invalid expiry value: invalid expiry' );
169		ExpiryDef::normalizeUsingMaxExpiry( 'invalid expiry', '6 months', TS_ISO_8601 );
170	}
171}
172