create(['role' => 'staff']); $client = Client::factory()->create(); HistoryEntry::factory(3)->create(['client_id' => $client->id, 'author_id' => $user->id]); $response = $this->actingAs($user)->getJson("/api/clients/{$client->id}/history"); $response->assertStatus(200)->assertJsonCount(3, 'data'); }); it('staff can add a history entry', function () { $user = User::factory()->create(['role' => 'staff']); $client = Client::factory()->create(); $type = HistoryType::factory()->create(['name' => 'Bemerkung']); $response = $this->actingAs($user)->postJson("/api/clients/{$client->id}/history", [ 'history_entry_type_id' => $type->id, 'body' => 'Rückruf vereinbart.', ]); $response->assertStatus(201) ->assertJsonPath('data.history_entry_type_id', $type->id) ->assertJsonPath('data.type_name', 'Bemerkung') ->assertJsonPath('data.requires_datetime', false) ->assertJsonPath('data.body', 'Rückruf vereinbart.') ->assertJsonPath('data.author_id', $user->id); $this->assertDatabaseHas('history_entries', [ 'client_id' => $client->id, 'history_entry_type_id' => $type->id, ]); }); it('stores and echoes back the occurred_at datetime unchanged', function () { $user = User::factory()->create(['role' => 'staff']); $client = Client::factory()->create(); $type = HistoryType::factory()->requiresDatetime()->create(['name' => 'Telefonat']); $response = $this->actingAs($user)->postJson("/api/clients/{$client->id}/history", [ 'history_entry_type_id' => $type->id, 'body' => 'Telefonat geführt.', 'occurred_at' => '2026-06-25T14:30:00', ]); $response->assertStatus(201) ->assertJsonPath('data.type_name', 'Telefonat') ->assertJsonPath('data.requires_datetime', true) ->assertJsonPath('data.occurred_at', '2026-06-25T14:30:00'); }); it('requires occurred_at when the type requires a datetime', function () { $user = User::factory()->create(['role' => 'staff']); $client = Client::factory()->create(); $type = HistoryType::factory()->requiresDatetime()->create(); $this->actingAs($user)->postJson("/api/clients/{$client->id}/history", [ 'history_entry_type_id' => $type->id, 'body' => 'Ohne Zeit.', ])->assertStatus(422)->assertJsonValidationErrors('occurred_at'); }); it('rejects an invalid history_entry_type_id', function () { $user = User::factory()->create(['role' => 'staff']); $client = Client::factory()->create(); $this->actingAs($user)->postJson("/api/clients/{$client->id}/history", [ 'history_entry_type_id' => 99999, 'body' => 'test', ])->assertStatus(422)->assertJsonValidationErrors('history_entry_type_id'); }); it('lets the author edit their own entry', function () { $user = User::factory()->create(['role' => 'staff']); $client = Client::factory()->create(); $entry = HistoryEntry::factory()->create([ 'client_id' => $client->id, 'author_id' => $user->id, 'body' => 'Original', ]); $response = $this->actingAs($user)->putJson("/api/history/{$entry->id}", [ 'body' => 'Updated body', ]); $response->assertStatus(200)->assertJsonPath('data.body', 'Updated body'); $this->assertDatabaseHas('history_entries', ['id' => $entry->id, 'updated_by' => $user->id]); }); it('forbids a non-author from editing an entry', function () { $author = User::factory()->create(['role' => 'staff']); $other = User::factory()->create(['role' => 'staff']); $client = Client::factory()->create(); $entry = HistoryEntry::factory()->create([ 'client_id' => $client->id, 'author_id' => $author->id, 'body' => 'Original', ]); $this->actingAs($other)->putJson("/api/history/{$entry->id}", [ 'body' => 'tampered', ])->assertStatus(403); }); it('nobody can edit a field_change audit entry', function () { $admin = User::factory()->create(['role' => 'admin']); $client = Client::factory()->create(); $entry = HistoryEntry::factory()->create([ 'client_id' => $client->id, 'author_id' => $admin->id, 'history_entry_type_id' => null, 'type' => HistoryEntryType::FieldChange->value, ]); $this->actingAs($admin)->putJson("/api/history/{$entry->id}", ['body' => 'tampered'])->assertStatus(403); }); it('renders a field_change entry as Stammdatenänderung', function () { $user = User::factory()->create(['role' => 'staff']); $client = Client::factory()->create(); HistoryEntry::factory()->create([ 'client_id' => $client->id, 'author_id' => $user->id, 'history_entry_type_id' => null, 'type' => HistoryEntryType::FieldChange->value, ]); $this->actingAs($user)->getJson("/api/clients/{$client->id}/history") ->assertStatus(200) ->assertJsonPath('data.0.is_field_change', true) ->assertJsonPath('data.0.type_name', 'Stammdatenänderung'); }); it('admin can soft-delete a history entry', function () { $admin = User::factory()->create(['role' => 'admin']); $client = Client::factory()->create(); $entry = HistoryEntry::factory()->create(['client_id' => $client->id, 'author_id' => $admin->id]); $response = $this->actingAs($admin)->deleteJson("/api/history/{$entry->id}"); $response->assertStatus(204); $this->assertSoftDeleted('history_entries', ['id' => $entry->id]); }); it('staff cannot delete a history entry', function () { $user = User::factory()->create(['role' => 'staff']); $client = Client::factory()->create(); $entry = HistoryEntry::factory()->create(['client_id' => $client->id, 'author_id' => $user->id]); $this->actingAs($user)->deleteJson("/api/history/{$entry->id}")->assertStatus(403); $this->assertDatabaseHas('history_entries', ['id' => $entry->id, 'deleted_at' => null]); });