Unit testing with Symfony's flashBag sessions - symfony

I'm creating an application in Silex with unit tests.
Running unit tests works fine against the regular session handler:
$app->register(new Silex\Provider\SessionServiceProvider(), array(
'session.storage.options' => array(
'cookie_lifetime' => 1209600, // 2 weeks
),
));
and setting this flag in my unit tests:
$this->app['session.test'] = true;
If I don't set that session.test flag, my unit tests throw a headers already sent error and all fail. With it on, my tests run well.
The issue is I am attempting to use the flashBag feature (session info that lasts only until first request then get removed):
$foo = $app['session']->getFlashBag()->all();
The flashBag does not seem to respect the session.test flag, and attempts to send headers, which cause all my unit tests to fail:
24)
Yumilicious\UnitTests\Validator\PersonAccountTest::setConstraintsPassesWithMinimumAttributes
RuntimeException: Failed to start the session because headers have
already been sent.
/webroot/yumilicious/vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php:142
/webroot/yumilicious/vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php:262
/webroot/yumilicious/vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/Session/Session.php:240
/webroot/yumilicious/vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/Session/Session.php:250
/webroot/yumilicious/src/app.php:38
/webroot/yumilicious/tests/Yumilicious/UnitTests/Base.php:13
/webroot/yumilicious/vendor/silex/silex/src/Silex/WebTestCase.php:34
/webroot/yumilicious/vendor/EHER/PHPUnit/src/phpunit/phpunit.php:46
/webroot/yumilicious/vendor/EHER/PHPUnit/bin/phpunit:5
I've narrowed it down to this bit of code: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php#L259
Specifically, line 262. Commenting out that single line allows my tests to work properly and all pass green.
I've searched quite a bit to get this to work, but am not having any luck. I think it's because the flashBag stuff is new (https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/Session/Session.php#L305) and the old methods are being deprecated.
Any suggestions on getting my unit tests to work would be awesome.

For testing you need to replace the session.storage service with an instance of MockArraySessionStorage:
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
$app['session.storage'] = new MockArraySessionStorage();
This is because the native one tries to send a cookie via header which of course fails in a test environment.
EDIT: There is now a session.test parameter that you should set to true. That will automatically make the session use a mock storage.

I had this happen too, if i am not mistaking i fixed by having my unittests run via a different environment, wich has
framework:
test: ~
session:
storage_id: session.storage.mock_file
set in the config_test.yml

I came across similar problem today and temp fix would be to comment out block of code in
\Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage
in start() method
/*
if (ini_get('session.use_cookies') && headers_sent()) {
throw new \RuntimeException('Failed to start the session because headers have already been sent.');
}
*/
This solution keeps tests "green" and from looks of it the application session functionality as is.

Related

Evernote IOS SDK fetchResourceByHashWith throws exception

Working with Evernote IOS SDK 3.0
I would like to retrieve a specific resource from note using
fetchResourceByHashWith
This is how I am using it. Just for this example, to be 100% sure about the hash being correct I first download the note with a single resource using fetchNote and then request this resource using its unique hash using fetchResourceByHashWith (hash looks correct when I print it)
ENSession.shared.primaryNoteStore()?.fetchNote(withGuid: guid, includingContent: true, resourceOptions: ENResourceFetchOption.includeData, completion: { note, error in
if error != nil {
print(error)
seal.reject(error!)
} else {
let hash = note?.resources[0].data.bodyHash
ENSession.shared.primaryNoteStore()?.fetchResourceByHashWith(guid: guid, contentHash: hash, options: ENResourceFetchOption.includeData, completion: { res, error in
if error != nil {
print(error)
seal.reject(error!)
} else {
print("works")
seal.fulfill(res!)
}})
}
})
Call to fetchResourceByHashWith fails with
Optional(Error Domain=ENErrorDomain Code=0 "Unknown error" UserInfo={EDAMErrorCode=0, NSLocalizedDescription=Unknown error})
The equivalent setup works on Android SDK.
Everything else works so far in IOS SDK (chunkSync, auth, getting notebooks etc.. so this is not an issue with auth tokens)
would be great to know if this is an sdk bug or I am still doing something wrong.
Thanks
This is a bug in the SDK's "EDAM" Thrift client stub code. First the analysis and then your workarounds.
Evernote's underlying API transport uses a Thrift protocol with a documented schema. The SDK framework includes a layer of autogenerated stub code that is supposed to marshal input and output params correctly for each request and response. You are invoking the underlying getResourceByHash API method on the note store, which is defined per the docs to accept a string type for the contentHash argument. But it turns out the client is sending the hash value as a purely binary field. The service is failing to parse the request, so you're seeing a generic error on the client. This could reflect evolution in the API definition, but more likely this has always been broken in the iOS SDK (getResourceByHash probably doesn't see a lot of usage). If you dig into the more recent Python version of the SDK, or indeed also the Java/Android version, you can see a different pattern for this method: it says it's going to write a string-type field, and then actually emits a binary one. Weirdly, this works. And if you hack up the iOS SDK to do the same thing, it will work, too.
Workarounds:
Best advice is to report the bug and just avoid this method on the note store. You can get resource data in different ways: First of all, you actually got all the data you needed in the response to your fetchNote call, i.e. let resourceData = note?.resources[0].data.body and you're good! You can also pull individual resources by their own guid (not their hash), using fetchResource (use note?.resources[0].guid as the param). Of course, you may really want to use the access-by-hash pattern. In that case...
You can hack in the correct protocol behavior. In the SDK files, which you'll need to build as part of your project, find the ObjC file called ENTProtocol.m. Find the method +sendMessage:toProtocol:withArguments.
It has one line like this:
[outProtocol writeFieldBeginWithName:field.name type:field.type fieldID:field.index];
Replace that line with:
[outProtocol writeFieldBeginWithName:field.name type:(field.type == TType_BINARY ? TType_STRING : field.type) fieldID:field.index];
Rebuild the project and you should find that your code snippet works as expected. This is a massive hack however and although I don't think any other note store methods will be impacted adversely by it, it's possible that other internal user store or other calls will suddenly start acting funny. Also you'd have to maintain the hack through updates. Probably better to report the bug and don't use the method until Evernote publishes a proper fix.

How to fake DateTime in a phpunit test?

ssI write a unittest using Symfony's KernelTestCase and have to test for a functionality, which happens only at a certain tie of the day (extra early or extra late).
So when I let my test run a noon of course nothing happens. How can I fake my system time to pretend that it has a different time and my test case is triggered.
I tried around with Symfony's ClockMock class but it does not work.
https://symfony.com/doc/current/components/phpunit_bridge.html#clock-mocking
This is my test code:
use Symfony\Bridge\PhpUnit\ClockMock;
use \DateTime;
/**
* testUserAchievedEarlyBirdTrophy
* #group time-sensitive
*/
public function testUserAchievedEarlyBirdTrophy()
{
ClockMock::withClockMock(strtotime('2018-11-05 01:00:00'));
echo (new DateTime())->format('Y-m-d H:m:s');
$user8Id = $this->user8->getId();
$progressSaveRequest = new ProgressSaveRequest($user8Id, $this->content_1_1->getId());
$this->progressService->saveProgress($progressSaveRequest);
$this->assertTrue($this->loggerCreator->hasDebugThatContains(
'Early Bird'
));
}
the echo give me today's date: 2019-02-01 16:02:06
I also had the feeling that ClockMock is rather used to skip time for e.g. testing caching instead of sleep().
What am I doing wrong?
The listener configuration is in place in my phpunit.xml
Calling bin/simple-phpunit causes a lot of installation happening.
Can't I use the normal phpunit?
Are there any other options to fake the time of the day?
Following the link you included in your post, the first paragraph says:
The ClockMock class provided by this bridge allows you to mock the
PHP's built-in time functions time(), microtime(), sleep() and
usleep(). Additionally the function date() is mocked so it uses the
mocked time if no timestamp is specified. Other functions with an
optional timestamp parameter that defaults to time() will still use
the system time instead of the mocked time.
(Emphasis added.)
This means that your call of
echo (new DateTime())->format('Y-m-d H:m:s');
is expected to give the system time, not the mocked time.
Change it to
echo date('Y-m-d H:m:s');
in order to match the requirements of ClockMock and get the mocked time.
Note: I've never used ClockMock myself, but just looking at the documentation this should be a first step to see if this resolves your problem.

In Postman, how to define pm.iterationData() when NOT using Collection Runner?

My use case is simple: I have a Postman Collection with a few requests, one of them is:
GET www.example.com/stores?country={{country}}
Then a simple Test:
pm.test("Check number of stores", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.stores.length).to.equal(pm.iterationData.get("size"));
});
So everything is nice and merry with the following Collection data used in the Collection Runner:
country,size
UK,15
US,32
However when I simply want to run this via the main Postman window, obviously the request and the Test fails. I can set a collection variable country to SE, but I have no idea how to set size in pm.iterationData just to try if my request and test script is working fine for another "country" - without running the whole collection/iterations.
Thanks in advance for all the help!
I'm not sure if you can modify iteration data variable in runtime, but a workaround for this issue is to copy your request from the original folder into a new folder, then you can run the whole folder with only one request, you dun have then to run all the requests within the collection.

Swiftmailer says DoctrineDataCollector::serialize() must return a string or NULL

I have set up Swiftmailer for use in Symfony2 and the code below works fine in the first controller I tried it in.
$message = \Swift_Message::newInstance()
->setSubject('sent from coupon admin')
->setFrom( array($this->container->getParameter('outgoing_address')=>'Admin'))
->setTo($user->getEmail())
->setBody('X');
$this->get('mailer')->send($message);
But when I moved it to a second one, where I actually need it to work it throws three exceptions:
3/3 Doctrine\Bundle\DoctrineBundle\DataCollector\DoctrineDataCollector::serialize() must return a string or NULL
2/3 Symfony\Bundle\SwiftmailerBundle\DataCollector\MessageDataCollector::serialize() must return a string or NULL
1/3 Serialization of 'SimpleXMLElement' is not allowed
When I remove the last line with get('mailer') the second controller works fine, so it is definitely mailer related, I just can't work out why the first one isn't trying to serialize the Swiftmailer message but the second one is and then fails so hard. I've viewing this in dev mode at the moment if that makes a difference.
It turns out my problem was that the emil subject I was setting was actually a SimpleXMLElement rather than a string. And when in dev mode it tries to log everything, whilst in prod mode it doesn't so it never hit that error.

"An active access token must be used" (Dec 2012) && The proper way to set up Facebooks PHP-SDK for Ajax calls to get the user-id

This is giving me quite some headache. I have an page-tab-application, where DB-interaction uses the facebook-user-id to assign and save data and also to check user permissions. Until a weak ago everything was working fine, but now with the upcoming december-changes this setup doesnt work anymore:
config.php:
$facebook = new Facebook( array(
'appId' => $app_id,
'secret' => $app_secret,
'cookie' => true
));
index.php:
includes config.php and gets the signed request (not important for the question
javascript.js:
calls the read-user-status.php and handles the data
read-user-status.php:
gives json-response, includes config.php and calls the $facebook -> getUser()-function to get the uid
Even when called from the index.php directly after page-load, I sometimes get the uid and sometimes I don't. Strangly enough I usually have to wait a little until I reload the page and then it works again. But this isn't always the case. This all is just very strange to me.
EDIT: Should have mentioned that this call:
$uid = $facebook -> getUser();
if ($uid) {
try {
// Proceed knowing you have a logged in user who's authenticated.
$user_profile = $facebook -> api('/me');
} catch (FacebookApiException $e) {
error_log($e);
$uid = FALSE;
echo "EXCEPTION $e";
}
}
gives out "EXCEPTION An active access token must be used to query information about the current user".
I know there quite a lot of similar questions out there, but none of the answers were helpful to my particular (and probably to the new breaking changes relied) problem.
EDIT2: I now suppose that it is a sdk-bug (https://developers.facebook.com/bugs/238039849657148 , thanks to CBroe). Any recommendations for a work-around are of course very welcome.
EDIT 3, TEMPORARY SOLUTION
Everytime you make an ajax request, you post the token you get from the FB.getLoginStatus or FB.login and read it out in the php file and set it via $facebook -> setAccessToken. Not suitable in all circumstances (you definately need to use post), is slower and brings some security issues, but still works.
Sounds like you are affected by the bug I reported beginning of November, https://developers.facebook.com/bugs/238039849657148
They’ve confirmed it and say they’re working on a fix – but since the change is only a few days away now, they should hurry up a little …
I got this working by doing the following...
if(!$user){
$loginUrl = $facebook->getLoginUrl(array(
'scope' => 'email',
'redirect_uri' => $app_url
));
header('Location: ' . $loginUrl);
}
I also added my app to be integrated with:
Website with Facebook login
App on Facebook
Page Tab
try by adding access token to request.
$accessToken = $facebook->getAccessToken();
$user_profile = $facebook->api('/me?access_token=' . $accessToken);
I found a work-around for this, until it is fixed (which it seems like, wont be in time until the changes take place).
Everytime you make an ajax request, you post the token you get from the FB.getLoginStatus or FB.login and read it out in the php file and set it via $facebook -> setAccessToken. Not suitable in all circumstances (you definately need to use post), is slower and brings some security issues, but still works.
if it you are lucky and your version of php sdk still registers session variables than right after _graph method declaration:
//find this method below
protected function _graph ($path, $method = 'GET', $params = array ())
{
//paste right after _graph method declaration code below:
if (isset($_SESSION["fb_".$this->getAppId()."_access_token"]))
{
$this->setAccessToken($_SESSION["fb_".$this->getAppId()."_access_token"]);
}
//till here
//and you are good to go
//remember: your version of sdk must be registering access token variable in session
//right after ajax call
//i used git to get version before last commit of sdk published on github

Resources