1313
1414use PHPUnit \Framework \TestCase ;
1515use Symfony \Component \Mailer \DelayedEnvelope ;
16+ use Symfony \Component \Mailer \Envelope ;
1617use Symfony \Component \Mailer \Exception \TransportException ;
18+ use Symfony \Component \Mailer \SentMessage ;
1719use Symfony \Component \Mailer \Transport \SendmailTransport ;
20+ use Symfony \Component \Mailer \Transport \Smtp \Stream \ProcessStream ;
21+ use Symfony \Component \Mailer \Transport \TransportInterface ;
1822use Symfony \Component \Mime \Address ;
1923use Symfony \Component \Mime \Email ;
24+ use Symfony \Component \Mime \RawMessage ;
2025
2126class SendmailTransportTest extends TestCase
2227{
2328 private const FAKE_SENDMAIL = __DIR__ .'/Fixtures/fake-sendmail.php -t ' ;
2429 private const FAKE_FAILING_SENDMAIL = __DIR__ .'/Fixtures/fake-failing-sendmail.php -t ' ;
30+ private const FAKE_INTERACTIVE_SENDMAIL = __DIR__ .'/Fixtures/fake-failing-sendmail.php -bs ' ;
2531
2632 /**
2733 * @var string
@@ -49,9 +55,7 @@ public function testToString()
4955
5056 public function testToIsUsedWhenRecipientsAreNotSet ()
5157 {
52- if ('\\' === \DIRECTORY_SEPARATOR ) {
53- $ this ->markTestSkipped ('Windows does not support shebangs nor non-blocking standard streams ' );
54- }
58+ $ this ->skipOnWindows ();
5559
5660 $ mail = new Email ();
5761 $ mail
@@ -71,20 +75,9 @@ public function testToIsUsedWhenRecipientsAreNotSet()
7175
7276 public function testRecipientsAreUsedWhenSet ()
7377 {
74- if ('\\' === \DIRECTORY_SEPARATOR ) {
75- $ this ->markTestSkipped ('Windows does not support shebangs nor non-blocking standard streams ' );
76- }
78+ $ this ->skipOnWindows ();
7779
78- $ mail = new Email ();
79- $ mail
80- ->from ('from@mail.com ' )
81- ->to ('to@mail.com ' )
82- ->subject ('Subject ' )
83- ->text ('Some text ' )
84- ;
85-
86- $ envelope = new DelayedEnvelope ($ mail );
87- $ envelope ->setRecipients ([new Address ('recipient@mail.com ' )]);
80+ [$ mail , $ envelope ] = $ this ->defaultMailAndEnvelope ();
8881
8982 $ sendmailTransport = new SendmailTransport (self ::FAKE_SENDMAIL );
9083 $ sendmailTransport ->send ($ mail , $ envelope );
@@ -93,11 +86,90 @@ public function testRecipientsAreUsedWhenSet()
9386 }
9487
9588 public function testThrowsTransportExceptionOnFailure ()
89+ {
90+ $ this ->skipOnWindows ();
91+
92+ [$ mail , $ envelope ] = $ this ->defaultMailAndEnvelope ();
93+
94+ $ sendmailTransport = new SendmailTransport (self ::FAKE_FAILING_SENDMAIL );
95+ $ this ->expectException (TransportException::class);
96+ $ this ->expectExceptionMessage ('Process failed with exit code 42: Sending failed ' );
97+ $ sendmailTransport ->send ($ mail , $ envelope );
98+
99+ $ streamProperty = new \ReflectionProperty (SendmailTransport::class, 'stream ' );
100+ $ streamProperty ->setAccessible (true );
101+ $ stream = $ streamProperty ->getValue ($ sendmailTransport );
102+ $ this ->assertNull ($ stream ->stream );
103+ }
104+
105+ public function testStreamIsClearedOnFailure ()
106+ {
107+ $ this ->skipOnWindows ();
108+
109+ [$ mail , $ envelope ] = $ this ->defaultMailAndEnvelope ();
110+
111+ $ sendmailTransport = new SendmailTransport (self ::FAKE_FAILING_SENDMAIL );
112+ try {
113+ $ sendmailTransport ->send ($ mail , $ envelope );
114+ } catch (TransportException $ e ) {
115+ }
116+
117+ $ streamProperty = new \ReflectionProperty (SendmailTransport::class, 'stream ' );
118+ $ streamProperty ->setAccessible (true );
119+ $ stream = $ streamProperty ->getValue ($ sendmailTransport );
120+ $ innerStreamProperty = new \ReflectionProperty (ProcessStream::class, 'stream ' );
121+ $ innerStreamProperty ->setAccessible (true );
122+ $ this ->assertNull ($ innerStreamProperty ->getValue ($ stream ));
123+ }
124+
125+ public function testDoesNotThrowWhenInteractive ()
126+ {
127+ $ this ->skipOnWindows ();
128+
129+ [$ mail , $ envelope ] = $ this ->defaultMailAndEnvelope ();
130+
131+ $ sendmailTransport = new SendmailTransport (self ::FAKE_INTERACTIVE_SENDMAIL );
132+ $ transportProperty = new \ReflectionProperty (SendmailTransport::class, 'transport ' );
133+ $ transportProperty ->setAccessible (true );
134+
135+ // Replace the transport with an anonymous consumer that trigger the stream methods
136+ $ transportProperty ->setValue ($ sendmailTransport , new class ($ transportProperty ->getValue ($ sendmailTransport )->getStream ()) implements TransportInterface {
137+ private $ stream ;
138+
139+ public function __construct (ProcessStream $ stream )
140+ {
141+ $ this ->stream = $ stream ;
142+ }
143+
144+ public function send (RawMessage $ message , ?Envelope $ envelope = null ): ?SentMessage
145+ {
146+ $ this ->stream ->initialize ();
147+ $ this ->stream ->write ('SMTP ' );
148+ $ this ->stream ->terminate ();
149+
150+ return new SentMessage ($ message , $ envelope );
151+ }
152+
153+ public function __toString (): string
154+ {
155+ return 'Interactive mode test ' ;
156+ }
157+ });
158+
159+ $ sendmailTransport ->send ($ mail , $ envelope );
160+
161+ $ this ->assertStringEqualsFile ($ this ->argsPath , __DIR__ .'/Fixtures/fake-failing-sendmail.php -bs ' );
162+ }
163+
164+ private function skipOnWindows ()
96165 {
97166 if ('\\' === \DIRECTORY_SEPARATOR ) {
98167 $ this ->markTestSkipped ('Windows does not support shebangs nor non-blocking standard streams ' );
99168 }
169+ }
100170
171+ private function defaultMailAndEnvelope (): array
172+ {
101173 $ mail = new Email ();
102174 $ mail
103175 ->from ('from@mail.com ' )
@@ -109,9 +181,6 @@ public function testThrowsTransportExceptionOnFailure()
109181 $ envelope = new DelayedEnvelope ($ mail );
110182 $ envelope ->setRecipients ([new Address ('recipient@mail.com ' )]);
111183
112- $ sendmailTransport = new SendmailTransport (self ::FAKE_FAILING_SENDMAIL );
113- $ this ->expectException (TransportException::class);
114- $ this ->expectExceptionMessage ('Process failed with exit code 42: Sending failed ' );
115- $ sendmailTransport ->send ($ mail , $ envelope );
184+ return [$ mail , $ envelope ];
116185 }
117186}
0 commit comments