create(['email' => 'firma@example.test']); $viaContact = User::factory()->create(['email' => 'kontakt@example.test']); $orphan = User::factory()->create(['email' => 'niemand@example.test']); $companyA = Company::factory()->presseecho()->create(['email' => 'firma@example.test', 'name' => 'Erklärbar AG']); $companyB = Company::factory()->presseecho()->create(['email' => 'andere@example.test', 'name' => 'Kontakt GmbH']); Contact::factory()->for($companyB)->create(['email' => 'Kontakt@Example.test', 'portal' => $companyB->portal->value]); $companyC = Company::factory()->presseecho()->create(['email' => 'dritte@example.test', 'name' => 'Verwaist KG']); $viaCompany->companies()->attach($companyA->id, ['role' => 'responsible']); $viaContact->companies()->attach($companyB->id, ['role' => 'responsible']); $orphan->companies()->attach($companyC->id, ['role' => 'responsible']); $orphan->companies()->attach($companyA->id, ['role' => 'owner']); $this->artisan('company-memberships:audit', ['--since' => '2000-01-01']) ->expectsOutputToContain('Verwaist KG') ->doesntExpectOutputToContain('Erklärbar AG') ->doesntExpectOutputToContain('Kontakt GmbH') ->assertSuccessful(); // Ohne --classify bleibt alles manual. expect(DB::table('company_user')->where('granted_via', 'auto')->count())->toBe(0); $this->artisan('company-memberships:audit', ['--classify' => true, '--since' => '2000-01-01'])->assertSuccessful(); $via = fn (int $userId, int $companyId): string => DB::table('company_user')->where('user_id', $userId)->where('company_id', $companyId)->value('granted_via'); expect($via($viaCompany->id, $companyA->id))->toBe('auto') ->and($via($viaContact->id, $companyB->id))->toBe('auto') ->and($via($orphan->id, $companyC->id))->toBe('manual') ->and($via($orphan->id, $companyA->id))->toBe('manual'); expect(collect(Storage::disk('local')->files())->filter(fn (string $f) => str_starts_with($f, 'company-memberships-unexplained-')))->toHaveCount(1); }); test('classify previews its candidates and skips links created after the given deploy time (Audit 7.1)', function () { /** @var TestCase $this */ Storage::fake('local'); $legacy = User::factory()->create(['email' => 'alt@example.test']); $recent = User::factory()->create(['email' => 'neu@example.test']); $companyOld = Company::factory()->presseecho()->create(['email' => 'alt@example.test', 'name' => 'Altbestand AG']); $companyNew = Company::factory()->presseecho()->create(['email' => 'neu@example.test', 'name' => 'Frisch GmbH']); $legacy->companies()->attach($companyOld->id, ['role' => 'responsible', 'created_at' => '2026-01-10 08:00:00', 'updated_at' => '2026-01-10 08:00:00']); // Nach dem Deploy manuell vergeben: trägt ihre Herkunft bereits, darf nicht umgestuft werden. $recent->companies()->attach($companyNew->id, ['role' => 'responsible', 'granted_via' => 'manual', 'created_at' => '2026-09-20 09:00:00', 'updated_at' => '2026-09-20 09:00:00']); $this->artisan('company-memberships:audit', ['--classify' => true, '--since' => '2000-01-01', '--before' => '2026-09-19 00:00:00']) ->expectsOutputToContain('Umzustufen auf auto: 1 Zuordnungen vor 2026-09-19 00:00:00') ->expectsOutputToContain('Altbestand AG') ->expectsOutputToContain('1 Zuordnungen als automatisch (auto) markiert.') ->assertSuccessful(); $via = fn (int $userId, int $companyId): string => DB::table('company_user')->where('user_id', $userId)->where('company_id', $companyId)->value('granted_via'); expect($via($legacy->id, $companyOld->id))->toBe('auto') ->and($via($recent->id, $companyNew->id))->toBe('manual'); });