+Testing an artisan command that edits the database - php

I'm trying to test an artisan command that is designed to do some database maintenance.
In particular, it searches for records that don't have a column filled, and fill it.
This is a simplified version of the fire() method of the command:
public function fire()
{
$items = Item::all();
$total = $items->count();
$updated = 0;
foreach ($items as $item) {
if ($item->myColumn != 'x') {
$item->myColumn = 'x';
$item->save();
$updated++;
}
}
$this->info("total: $total updated: $updated");
}
My (acceptance) test is very simple and does the following:
insert a record in the db
call the artisan command
check that the inserted record has been updated
This is the code:
public function doTheTest(AcceptanceTester $I)
{
$I->wantTo('setup the myColumn when it is not set');
$id = $I->haveRecord('items', [
'myColumn' => '',
]);
$I->runShellCommand('php artisan items:updater');
$I->seeRecord('items', [
'id' => $id,
'myColumn' => 'x',
]);
}
However the test fails, and I get the following message:
Couldn't see record "items",{"id":101,"myColumn":"x"}:
Couldn't find items with {"id":101,"code":"x"}
As you can see, the id of the new record is 101, because there are already 100 items in the db dump, but what it is strange is that the $this->info() in the command prints
total: 100 updated: 100
as if the database used inside the test and the one used inside artisan are different.
Moreover, if at the end of the test I try to grab the added record, and print it, as shown in the following snippet
public function doTheTest(AcceptanceTester $I)
{
/* ... */
$item = $I->grabRecord('items', [
'id' => $id,
]);
\Codeception\Util\Debug::debug($item);
}
and run the codecept run acceptance --debug command, I get the added record
stdClass Object
(
[id] => 101
[myColumn] =>
)
I'm very confused, because the there is a single database, but I'm surely misunderstanding something important here.
Could anyone give me a help?
Thank you very much,

The issue is that every query using Laravel4 module is run in a transaction that, by default, will be rolled back at the end. If you take a look at the Config section of Laravel4 documentation it states
cleanup: boolean, default true - all db queries will be run in transaction, which will be rolled back at the end of test.
You can check this if you restart the MySQL server (in which case, when you run the tests again you'll still see the id 101), or take a look at MySQL logs that will have an entry like the following for each test:
150417 23:24:24 2 Connect root#localhost on laravel-test
2 Prepare set names 'utf8' collate 'utf8_unicode_ci'
2 Execute set names 'utf8' collate 'utf8_unicode_ci'
2 Close stmt
2 Query START TRANSACTION
2 Prepare insert into items (myColumn) values (?)
2 Execute insert into items (myColumn) values ('')
2 Close stmt
2 Query ROLLBACK
2 Quit
To fix this, you need to configure the option cleanup of Laravel4 module in your codeception.yml file, like this:
modules:
config:
Laravel4:
cleanup: false

Related

How to detect a failed transaction in neo4j-php-client

I’m using graphaware/neo4j-php-client 4.5.1 with Neo4j 3.0.4 on PHP 5.6.24.
I don’t understand how to find out whether a transaction has failed.
For example, I try to delete a node that still has relationships. If I run the DELETE in this simple query:
$client->run
(
'MATCH (node { name: {name} }) DELETE node',
[ 'name' => 'Fred' ]
);
… I get this exception, which is the behaviour I expected:
[GraphAware\Neo4j\Client\Exception\Neo4jException]
org.neo4j.kernel.api.exceptions.ConstraintViolationTransactionFailureException:
Cannot delete node<31>, because it still has relationships.
To delete this node, you must first delete its relationships.
But when I wrap that same query inside a transaction:
$transaction = $client->transaction();
$transaction->push
(
'MATCH (node { name: {name} }) DELETE node',
[ 'name' => 'Fred' ]
);
$results = $transaction->commit();
foreach ($results as $result)
{
$summary = $result->summarize();
$stats = $summary->updateStatistics();
printf("Nodes deleted: %d\n", $stats->nodesDeleted());
}
printf("Transaction status: %s\n", $transaction->status());
… Neo4j doesn’t delete the node, but I see this (suggesting success) instead of an exception:
Nodes deleted: 1
Transaction status: COMMITED
Am I missing anything, or is this a bug? Thanks in advance!
Thanks,
This is actually a bug and I fixed it in https://github.com/graphaware/neo4j-php-client/commit/af8f01475a3cf63549498449574eb9c4bb8e7254
The 4.5.3 version including this fix should be available on packagist in a couple of minutes.
Please test and report back.

MongoDB not insert in order with PHP Driver

There is a problem in inserting to MongoDB database. It is not insert to database in right order.
We read the writing concern in MongoDB:
http://www.php.net/manual/en/mongo.writeconcerns.php
We use mongoDB 2.6 and PHP driver 1.6 with following sample code:
set_message_sample('1');
set_message_sample('2');
set_message_sample($id) {
$connecting_string = sprintf('mongodb://%s:%d/%s', $hosts, $port,$database), // fill with right connection setting
$connection= new Mongo($connecting_string,array('username'=>$username,'password'=>$password)); // fill with proper authentication setting
$dbname = $connection->selectDB('dbname');
$collection = $dbname->selectCollection('collection');
$post = array(
'title' => $id,
'content' => 'test ' . $id,
);
$posts->insert($insert,array("w" => "1"));
Sometimes the result is inserting "2" before "1" to database. We want to inserting in right order (first "1" and next "2") all the times. I also notice that we order the collection with mongoID which automatically set by mongoDB.
We check many options but the problem not solved. How we could solve it? ( How we could disable something like cache or isolate the insert queue to MongoDB.)
So, i think you could insert the second only after the confirmation of the first one. since the insert is asynchronous, you can't be sure who goes first. So you must insert only after the confirmation of the first one.

How to update database in moodle using cron

I am facing a problem. I have to update a field in database by using cron function in moodle. I am using update query in cron function to update value. But It doesn't work. I am using this function to update value:
function activitysetmodule_cron ()
{
global $CFG, $DB;
$DB->update_record("activitysetmodule",)
$sql="update {$CFG->prefix}activitysetmodule as asm set status = 1 where exists (select 1 from {$CFG->prefix}course_modules as cm where (module=asm.activityset OR module=asm.activityset2 ) AND completion=1 AND asm.course =cm.course ");
return true;
}
Please help to sought it out.
Take a look at the documentation https://docs.moodle.org/dev/Data_manipulation_API#Updating_Records
$DB->update_record takes 2 params, the name of the table to update the record in and an object containing the updated data.
e.g.
$obj = new stdClass();
$obj->id = $id_of_object_to_update;
$obj->status = 1;
$DB->update_record('tablename', $obj);
It looks like you should refactor your code to get a list of records to update, then call $DB->update_record on each in turn (or $DB->set_field, if there is only one field to update). Alternatively, you could use the $DB->execute($sql) function to directly run some SQL on the server, e.g.
$DB->execute("UPDATE {activitysetmodule} asm SET status = 1 WHERE EXISTS (SELECT 1 FROM {course_modules} cm WHERE (module=asm.activityset OR module=asm.activityset2 ) AND completion=1 AND asm.course = cm.course)");
Note the use of {tablename} rather than {$CFG->prefix}tablename and the removal of the 'AS' keyword, as that is not allowed on all DB engines.
Note also, if you haven't done so already, turning on debugging (http://docs.moodle.org/en/Debugging) will give you much more helpful error messages.

PHP ldap_mod_replace fails with «Type or value exists» error

I have created a light Model Manager for LDAP over PHP's API to ease object managements from Active Directory.
Everything runs fine but I have a problem when updating multi valued attributes even if I change all the values, the transaction fails with «Type or value exists» error and the attribute is not changed in the database.
The test case I am using is to change de multi valued "description" field for a user. If I add new values or change the whole array of values, the transaction always fail.
The part of the code is the following:
if (count($mod_attr) > 0)
{
$ret = #ldap_mod_replace($this->getHandler(), $dn, $mod_attr);
if ($ret === false)
{ $this->log(sprintf("LDAP ERROR '%s' -- Modifying {%s}.", ldap_error($this->getHandler()), print_r($mod_attr, true)), \SlapOM\LoggerInterface::LOGLEVEL_CRITICAL);
throw new LdapException(sprintf("Error while MODIFYING values <pre>%s</pre> in dn='%s'.", print_r($mod_attr, true), $dn), $this->getHandler(), $this->error);
}
$this->log(sprintf("Changing attribute {%s}.", join(', ', array_keys($mod_attr))));
}
The complete code can be found [here on github](https://github.com/chanmix51/SlapOM/blob/master/lib/SlapOM/Connection.php#L115 [github]).
The logs show the following lines:
2013-06-04 10:39:54 | => MODIFY dn='CN=HUBERT Gregoire,OU=...
2013-06-04 10:39:54 | => LDAP ERROR 'Type or value exists' -- Modifying {Array
(
[description] => Array
(
[0] => Description 2
[1] => Description 3
)
)}
Even if the preceding values were ["description" => ['Description 1']]. Is there something I am not getting or doing wrong ?
The answer is short: «Description is not a multi valued field». As usual, the error message was so confusing, it lead me to spend hours on the wrong problem.
In short: the LDAP error 20 «Type or value exists» can be either you are trying to insert twice the same values in a multi valued field or you are trying to insert several values in a single valued field.

Save inside loop

I created a function that has a infinite loop and a delay at the end to make sure it will only execute one time per second.
This function is in a shell class and its goal is to save/update records in three different tables and I invoque it from the console line using the command 'cake className'
My problem is:
I stop the loop at the console (Ctrl + C) and only the last record is saved in the database.
I don't know if there is some problems with transaction, i tried to use begin() before save and commit after, but the problem subsisted.
The code is something like this:
$this->T1->begin();
$this->T2->begin();
$this->T3->begin();
if ($this->T1->save((array (
'Field1' => $val1,
'Field2' => $val2,
'Field3' => $val3)))
&& $this->T2->save(array (
'Field1' => $val4,
'Field2' => $val5,
'Field3' => $val6)))
&& $this->T3->saveField('Field1', $val7))
{
$this->T1->commit();
$this->T2->commit();
$this->T3->commit();
echo 'success message';
}
It could be because the id is still present in each of the models which often happens when saving in a loop because the data gets merged.
Try the following to reset the models so they don't load in the previous data.
$this->Sale->create(false);
$this->Bidagent->create(false);
$this->Licitation->create(false);
From your code snippet though i'm not sure what T1, T2 and T3 are... if they are models then they need the same $this->Model->create(false);
Reference:
http://api.cakephp.org/2.3/class-Model.html#_create

Categories