Laravel Test intermittently failing due to 1 second difference - laravel

I have the following type of test for most models across my application:
* #test
*/
public function an_authorised_user_can_delete_a_contact()
{
$contact = Contact::factory()->create();
$this->assertDatabaseHas('contacts', $contact->getAttributes());
$response = $this->actingAs($this->user_delete)
->delete('/contacts/'.$contact->id);
$this->assertSoftDeleted('contacts', $contact->getAttributes());
$response->assertRedirect('contacts');
}
This works well most of the time, but every now and then will fail due to the timestamps being slightly out.
Failed asserting that any soft deleted row in the table [contacts] matches the attributes {"first_name":"Katherine","last_name":"Will","title":"Jewelry Model OR Mold Makers","telephone":"+15127653255","mobile":"+19366193055","email":"lucy.lind#example.net","vendor_id":1,"updated_at":"2022-04-04 18:09:50","created_at":"2022-04-04 18:09:50","id":1}.
Found: [
{
"id": 1,
"first_name": "Katherine",
"last_name": "Will",
"title": "Jewelry Model OR Mold Makers",
"telephone": "+15127653255",
"mobile": "+19366193055",
"email": "lucy.lind#example.net",
"vendor_id": 1,
"created_at": "2022-04-04 18:09:50",
"updated_at": "2022-04-04 18:09:51",
"deleted_at": "2022-04-04 18:09:51"
}
]
The difference is in updated_at - "2022-04-04 18:09:50" vs "2022-04-04 18:09:51".
Is there a better way to structure the test to make it more robust?

1st solution:
Use Carbon::setTestNow('2022-04-04 18:09:50') , this will make timestamps not change.
2nd solution (recommended):
Since you wrote the test to check if an authorized user can delete a contact I would not check all attributes as you did but instead would assert the model itself, which is recommended on laravel documentation, here's the link:
$this->assertSoftDeleted($contact);
or only check with the id
$this->assertSoftDeleted('contacts', [
'id' => $contact->id
]);
You can also check out this answer on laracast forum maybe it well help you more.

After deleting the contact record, you should fetch a fresh instance of the model. Try this:
...
$deletedContact = $contact->fresh();
$this->assertSoftDeleted('contacts', $deletedContact->getAttributes());
$response->assertRedirect('contacts');
I would also remove $this->assertDatabaseHas('contacts', $contract->getAttributes()); to make the test run a bit faster. When writing tests you should be testing your own code, not the Laravel framework.

In your factory try to override the "created_at" attribute an hour ago.
public function definition()
{
return [
'created_at'=> $this->faker->dateTimeBetween('-1 hour' );,
'updated_at'=> \Carbon\Carbon::now()->timestamp;
];
}

Related

Is there an API that shows the user task histroy on the Camunda side?

I have a Camunda flow, there are 2-3 user tasks in this flow. I want to see their history after completing these tasks. There are a couple of methods, but I just want to get both the label and the entered value with rest-api.
I can't get them directly with rest-api.
The following method returns variables with the processInstanceId.
List<HistoricVariableInstance> instances = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(processIntanceId)
.list();
but I need to call another rest-api to get the labels. GET /process-definition/{id}/xml with this api.
Other topics have been opened for this, but it does not meet exactly what I want.
similar question
I think you are right, you need 2 steps. I would combine the following requests:
First get all User Tasks:
GET /history/task -see API Reference
From its result Array you need the id and the name (which is the label):
[{"id":"anId",
...
"name":"aName",
...
}]
Now you can get the variables for each UserTask, like
GET /history/variable-instance?taskIdIn=YourTaskId see API Reference
https://docs.camunda.org/manual/7.16/reference/rest/history/variable-instance/post-variable-instance-query/
returns the name (label) and the value of the process variables
[
{
"id": "someId",
"name": "someVariable",
"type": "Integer",
"variableType": "integer",
"value": 5,
"valueInfo": {},
"processDefinitionKey": "aProcessDefinitionKey",
"processDefinitionId": "aProcessDefinitionId",
"processInstanceId": "aProcInstId",
"executionId": "aExecutionId",
"activityInstanceId": "aActivityInstId",
"caseDefinitionKey": null,
"caseDefinitionId": null,
"caseInstanceId": null,
"caseExecutionId": null,
"taskId": null,
"tenantId": null,
"errorMessage": null,
"state": "CREATED",
"createTime":"2017-02-10T14:33:19.000+0200",
"removalTime": "2018-02-10T14:33:19.000+0200",
"rootProcessInstanceId": "aRootProcessInstanceId"
}
]

Laravel: with method on a collection

I have a Laravel App. In this I have the models Tasks and Comments. Its a manyToOne relation. One task can have many comments.
I want to give back all tasks with the attached comments.
Im able to do it with one task. Example
$Task = Task::find($id)->with("comments")->get();
Its returning me the Task with the field "comments". In this field is an array of all comments.
{
"id": 1,
"type": "Aufgabe",
"comments": [
{
"id": 1
},
{
"id": 2
},
{
"id": 3
}
]
}
But now I want to get ALL tasks with the comments attached. Im getting all tasks with:
Task::all()
But my problem now, its a collection of tasks and this collection dont have the comments() or with() command.
"message": "Method Illuminate\\Database\\Eloquent\\Collection::with does not exist.",
I could iterate through all the tasks, call with() on each of them and then build the answer by my own, but I think there needs to be a way to do it Laraval thats quick and a one-liner

How should I use redis?

I'm doing an api in laravel and I want to use Redis. I'm not sure if I'm doing well.
How should I use Redis to store json and then get a value by ID or another column.
Should I save the json as plain text? Then how do I go through it to look for a specific value or how you should keep the values?
{
"id": 1,
"name": "Package A",
"dimensions": "100x100x100",
"created_at": "2019-05-29 01:35:53",
"updated_at": "2019-05-29 01:35:53"
},
{
"id": 2,
"name": "Package B",
"dimensions": "150x150x150",
"created_at": "2019-05-29 01:36:53",
"updated_at": "2019-05-29 01:36:53"
}
$package = Package::where->get();
Redis::hmset('packages', array($package));
Or I shoud ..
$package = Package::where->get();
Redis::set('packages', serialize($package));
Then How I get a value?
For example: all fields id=2
I recommend saving the information in the following way, so it is easier to access later.
Package::all()->each(function($package){
Redis::set("package:{$package->id}", $package->toJson());
});
And if you need to recover information :
json_decode(Redis::get("package:$id"), true);
In redis you have to consider how to access the information, and then how to save it. For example, if you want to search by name, your key will be composed of the same name.
If you want retrive all packages from Redis, you will need all of packages key´s.
The folowing command retrive all keys matches with the structure :
Redis::keys('package:*');
After that, you can do the following
$keys = Redis::keys('package:*');
$packages = [];
foreach ($keys as $key){
array_push($packages, json_decode(Redis::get($key), true));
}
return $packages;

Laravel how to validation with unique JSON fields type?

I have a POST endpoint on my Laravel 5.7 API and Mariadb 10.3, I used postman to test my route and here is the problem. I want to send an array of the same kind of object, like this :
{
"Shops":[
{
"name": {
"en":"ShopEng",
"es":"ShopESP"
},
"code": "0891"
}
]
}
In my database.I have Shops table and Name field as JSON type.
{"en":"TestEng","es":"TestESP"}
In my Request. I also tried with the simple but not work.
public function rules()
{
return array(
'Shops' => 'required|array',
'Shops.*.name.en' => 'required|unique:shops,name->en',
'Shops.*.name.es' => 'required|unique:shops,name->es',
'Shops.*.code' => 'required|integer'
);
}
With Message.
"message": "Method Illuminate\\Validation\\Validator::validateUnique,shops,name>en does not exist.",
"exception": "BadMethodCallException",
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Validation/Validator.php",
The second parameter written in the unique validation rule refers to a column on the database table where the unique rule will be tested, I'm afraid the syntax you wrote there is not valid.
My guess here is that you will have yo create a custom validation rule for handling this particular case.

Rethinkdb: Including a subdocument for nested doc

I am performing an operation, and it works, but I want to know if there is a better or more efficient way to do what I want.
I have an object in my db that looks like this:
{
"id": "testId",
"name": "testName",
"products": [
{
"name": "product1"
"info": "sampleInfo",
"templateIds": [
"asdf-1",
"asdf-2"
]
},
{
"name": "product2"
"info": "sampleInfo",
"templateIds": [
"asdf-1",
"asdf-2"
]
}
]
}
As you can see, each "product" in the "products" array has a sub-array of templateIds. These match templates stored in another table. What I want to do is create a query that merges those templates onto each product object before I send it all back.
Currently I am doing this with sub-merges:
r.table('suites').get('testId').merge(function(suite){
return {
products: suite('products').merge(function(product){
return {
templates: r.expr(product('templateIds')).map(function(id) {
return r.table('templates').get(id)
})
}
})
}
})
My question is: is there a more efficient way to do this? Or is there a completely different way of thinking I should employ to do this?
Thanks guys!
That looks right to me. The only thing I can think of is that r.table('templates').get_all(r.args(product('templateIds'))) is shorter than product('templateIds').map(function(id){ return t.table('templates').get(id);}) and might well be faster.
EDIT: If you have a small number of templates, another thing that would make this run faster would be to do the substitution in the client instead and cache the retrieved templates by ID. RethinkDB will have to do a separate read for each template ID, even if it sees the same one over and over again, because it doesn't know enough to know whether or not caching those values is safe.

Resources