phpunit You cannot serialize or unserialize PDO instances - phpunit

I'm using PHPUnit with processIsolation="true" because I need to set cookie value in the code I'm testing, and without processIsolation="true" it can't be done. But in one of my test case, I'm getting the error:
PHPUnit_Framework_Exception: [10-May-2018 15:23:28 UTC] PHP Fatal error: Uncaught PDOException: You cannot serialize or unserialize PDO instances in -:394
Stack trace:
#0 [internal function]: PDO->__sleep()
#1 -(394): serialize(Array)
#2 -(536): __phpunit_run_isolated_test()
#3 {main}
thrown in - on line 394
Test case:
public function testUserCookieIsSavedToClicksTable()
{
$cookie = sha1('12345');
$_COOKIE['user_cookie'] = $cookie;
$offerId = 1;
$tx = $this->getTx($offerId);
// Go
$this->controller = new ClickController(new Click(new Database(Cache::getInstance()), new Queue, new RedisStorage()));
$this->runLocal([1, $this->userId], null);
$sql = 'select * from clicks order by id desc limit 1';
$click = $this->db->query($sql, [], 'fetch');
$this->assertEquals($tx, $click['tx_id']);
$this->assertEquals($click['user_cookie'], $cookie);
}
This error is thrown when calling any of $this->assertEquals($tx, $click['tx_id']); or $this->assertEquals($click['user_cookie'], $cookie);. But I can var_dump any variables used in those assertions.
I tried all solutions from here but they not working for me

Process Isolation require that all objects in the process to isolate can be serialized / unserialized.
The error you see is about a specific object that does not support PHP object serialization / unserialization.
That first of all means in the case you ask about, that you can not make use of process isolation for that test.
If you chew on that a bit and change some thoughts about what is going on here and why some objects (like database connections) normally can not be serialized and then as well, why process isolation might make use of serialization as well, it perhaps comes to mind as well why process isolation for database connections is not such a good idea after all.
So even though you see a technical limitation here that is causing the error, in the end I'd say it makes perfect sense to not run database connection based tests in isolated processes as part of the same testsuite.
This is perhaps not the answer you hoped for.
Perhaps divide and conquer can make sense here: Put those tests for which process isolation work into a testsuite of it's own and configure process isolation for it.
For the other one do the same into a testsuite of their own as well.
The PHPUnit XML configuration file offers settings to make these, sometimes it is even needed to have multiple of these XML configuration files, depends a bit on the Phpunit version used, but also for configuration settings for the test-runner.

This is probably error in your db component. It should implement __sleep() method to make sure, that PDO instance is not serialized with it (and probably implement __wakeup() to reconnect after deserialization).
You didn't share any informaction about db component, but this could be achieved by something like this:
public function __sleep() {
$fields = (array) $this;
unset($fields['pdo']);
return array_keys($fields);
}

Related

Doctrine: atomic updates and exceptions in a loop

We are migrating a project from a more basic ORM to using Symfony+Doctrine. In the project we have a lot of cron jobs looking like this:
$rows = $someRepository->getRows();
foreach ($rows as $row) {
try {
$db->beginTransaction(); //simple begin transaction in db
//do some handling of data
// Maybe load some other entities and update those
// ...
$db->commit();
} catch (Throwable $t) {
//log error
//clear entity cache
$db->rollback(); //simple rollback in db
}
}
When we did it this way, all changes within the try catch was atomic while it at the same time was possible to recover from an error and continue on the next $row.
In Symfony+Doctrine, I simply cannot figure out how to mimic this behaviour. The recommendation from Doctrine to handle an exception is closing the EntityManager, but how do you recover?
The ORM does this implicitly on flush, so most of the time you can avoid the hassle of doing so on your own.
However, if you want clear demarcation you can still do it explicitly, in a similar manner you did so far.
More reading and examples here: https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/transactions-and-concurrency.html
EDIT related to the comment below:
Instead of injecting the manager, you should inject the registry.
After that on catch, you can check if the $em->isOpen(), and call $registry->resetManager() if not.
I suspect this will also reset the unit of work, so you might encounter detached entities. In that case you should do $em->merge();
One thing to note here is that an expection is not considered normal in doctrine, so they are closing the manager because of that. You might think that this is overcompicated - yes it is, because you are working against the philosophy here. Validate your data if you can. Read this section: https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/transactions-and-concurrency.html#exception-handling
As for the why: (This is not offical, just based on my knowledge) The managers internal unit of work is a stateful object. When an exception occures during a transaction that state will remain the same, but couln't be persisted to the database. If they let this go that would mean the EM would try to apply all state changes again, and would encounter the same exception again. So no point in leaving it open in the same state, a reset is needed.

Doctrine find() and querybuilder() return different result in PHPUnit test

With Doctrine and Symfony in my PHPUnit test method :
// Change username for user #1 (Sheriff Woody to Chuck Norris)
$form = $crawler->selectButton('Update')->form([
'user[username]' => 'Chuck Norris',
]);
$client->submit($form);
// Find user #1
$user = $em->getRepository(User::class)->find(1);
dump($user); // Username = "Sheriff Woody"
$user = $em->createQueryBuilder()
->from(User::class, 'user')
->andWhere('user.id = :userId')
->setParameter('userId', 1)
->select('
user
')
->getQuery()
->getOneOrNullResult()
;
dump($user); // Username = "Chuck Norris"
Why my two methods to fetch the user #1 return different results ?
diagnosis / explanation
I assume* you already created the User object you're editing via crawler before in that function and checked that it is there. This leads to it being a managed entity.
It is in the nature of data, to not sync itself magically with the database, but some automatism must be in place or some method executed to sync it.
The find() method will always try to use the cache (unless explicitly turned off, also see side note). The query builder won't, if you explicitly call getResult() (or one of its varieties), since you explicitly want a query to be executed. Executing a different query might lead to the cache not being hit, producing the current result. (it should update the first user object though ...) [updated, due to comment from Arno Hilke]
((( side note: Keeping objects in sync is hard. It's mainly about having consistency in the database, but all of ACID is wanted. Any process talking to the database should assume, that it only is working with the state at the moment of its first query, and is the only user of the database. Unless additional constraints must be met and inconsistent reads can occur, in which case isolation levels should be raised (See also: transactions or more precisely: isolation). So, automatically syncing is usually not wanted. Doctrine uses certain assumptions for performance gains (mainly: isolation / locking is optimistic). However, in your particular case, all of those things are of no actual concern... since you actually want a non-repeatable read. )))
(* otherwise, the behavior you're seeing would be really unexpected)
solution
One easy solution would be, to actively and explicitly sync the data from the database by either calling $em->refresh($user), or - before fetching the user again - to call $em->clear(), which will detach all entities (clearing the cache, which might have a noticable performance impact) and allowing you to call find again with the proper results being returned.
Please note, that detaching entities means, that any object previously returned from the entity manager should be discarded and fetched again (not via refresh).
alternate solution 1 - everything is requests
instead of checking the database, you could instead do a different request to a page that displays the user's name and checks that it has changed.
alternate solution 2 - using only one entity manager
using only one entity manager (that is: sharing the entity manager / database in the unit test with the server on the request) may be a reasonable solution, but it comes with its own set of problems. mainly omitted commits and flushes may avoid detection.
alternate solution 3 - using multiple entity managers
using one entity manager to set up the test, since the server is using a new entity manager to perform its work, you should theoretically - to do this actually properly - create yet another entity manager to check the server's behavior.
comment: the alternate solutions 1,2 and 3 would work with the highest isolation level, the initial solution probably wouldn't.

Connect to SQLite database in Slick doesn't work but doesn't throw an error

So the documentation for Typesafe's Slick is very thin and its examples are for Play which doesn't help very much working in Eclipse.
I try to connect to an existing SQLite database on my system, which consists of one table "Maintenance_Request".
import slick.driver.SQLiteDriver.api._
import scala.concurrent.ExecutionContext.Implicits.global
object starter {
def main(args: Array[String]): Unit = {
val db = Database.forURL("jdbc:sqlite:/home/sq/workspace/dbFun/IOdb.db", driver = "org.sqlite.JDBC")
val action = sql"select CATEGORY from MAINTENANCE_REQUEST".as[(Int)]
db.run(action).foreach(println)
}
}
Starting the program doesn't give me any result though. Also if i change the path, like leaving out a character, so it is not a valid path, doesn't throw an error! So i don't have a clue what is working and what is not working.
Is there a way of knowing , if variable db is connected to a database?
Any way of knowing that Database.forURL worked or failed??
I suspect the reason you see no result is just that your main program is completing before the query has completed.
What I mean is, the type of db.run(action) will be Future[Seq[Int]]. Calling foreach on that does not block for a result. The behaviour of foreach on a Future is noted here:
It is important to note that calling the foreach combinator does not block to traverse the value once it becomes available. Instead, the function for the foreach gets asynchronously executed only if the future is completed successfully.
-- http://docs.scala-lang.org/overviews/core/futures.html
So what you can do is await the result:
import scala.concurrent.Await
import scala.concurrent.duration._
val result = Await.result(db.run(action), 2 seconds)
result.foreach(println)
...which will ensure you see the result printed before your program terminates.
In terms of errors connecting to a database...
in forUrl, if the connection string is not a valid recognized scheme, you'll see an exception such as java.sql.SQLException: No suitable driver
if you give a valid URL, but the wrong database name, that is going to depend on the database in question. For H2, and I suspect SQLite, you'll probably create a new empty database. There may be parameters you can pass in the JDBC URL to control this behaviour, but that will be driver/database specific.

in Flex 3.2 Having troubles converting remote object result to specific object on client side in modules

in Flex 3.2 Having troubles converting remote object result to specific object on client side in modules.
For example I have VIPSAdmin module.
it has function
private function doResult(event:ResultEvent):void {
var data_:Array = ArrayUtil.toArray(event.result);
var result:ResultDTO = data_[0] as ResultDTO;
if(!result.isError()) {
trace(result.result);
vipsAdminDTO = result.result as VIPSAdmin;
compId= vipsAdminDTO.compId; // second time dying here
}
}
Function invoked when I get data from remote objet.
First time all great,when I unload this modeule and load it again:
data_[0] as ResultDTO;
Performs fine, but
vipsAdminDTO = result.result as VIPSAdmin;
vipsAdminDTO always null!
Even when
trace(result.result);
produces [object VIPSAdmin]
What a heck I missing here!? Looks like it just cannot do
result.result as VIPSAdmin;
even when trace and debug says it is instance of VIPSAdmin
I've figured out what is the problem, problem is that when I first instantiate something in module then in main app, somehow classes are not alined even that they are identical !
So solution is to make a fake instance in application class first, then if you use that same class to make an instance in module it will work!
I do it very simple in my main application class I just added:
VIPSAdmin;
This seems to create some sort of ghost instance, which I belie will be pickup by GC later, but will build tables of instances properly! Which solved my problem.
Not sure if this is appropriate solution ! but it sure works.

How to use MarkLogic xquery to tell if a document is 'in-memory'

I want to tell if an XML document has been constructed (e.g. using xdmp:unquote) or has been retrieved from a database. One method I have tried is to check the document-uri property
declare variable $doc as document-node() external;
if (fn:exists(fn:document-uri($doc))) then
'on database'
else
'in memory'
This seems to work well enough but I can't see anything in the MarkLogic documentation that guarantees this. Is this method reliable? Is there some other technique I should be using?
I think that behavior has been stable for a while. You could always check for the URI too, as long as you expect it to be from the current database:
xdmp:exists(fn:doc(fn:document-uri($doc)))
Or if you are in an update context and need ACID guarantees, use fn:exists.
The real test would be to try to call xdmp:node-replace or similar, and catch the expected error. Those node-level update functions do not work on constructed nodes. But that requires an update context, and might be tricky to implement in a robust way.
If your XML document is in-memeory, you can use in-mem-update API
import module namespace mem = "http://xqdev.com/in-mem-update" at "/MarkLogic/appservices/utils/in-mem-update.xqy";
If your XML document exists in your database you can use fn:exists() or fn:doc-available()
The real test of In-memory or In-Db is xdmp:node-replace .
If you are able to replace , update , delete a node then it is in database else if it throws exception then it's not in database.
Now there are two situation
1. your document is not created at all:
you can use fn:empty() to check if it is created or not.
2. Your document is created and it's in memory:
if fn:empty() returns false and xdmp:node-replace throws exception then it's in-memory

Resources