fixed tests

This commit is contained in:
philipp lang 2022-11-16 23:39:44 +01:00
parent bc367b2256
commit 0b344a2ec4
18 changed files with 381 additions and 151 deletions

View File

@ -0,0 +1,55 @@
<?php
namespace App\Member\Actions;
use App\Actions\MemberPullAction;
use App\Activity;
use App\Confession;
use App\Member\Member;
use App\Setting\NamiSettings;
use App\Subactivity;
use Lorisleiva\Actions\Concerns\AsAction;
class NamiPutMemberAction
{
use AsAction;
public function handle(Member $member, ?Activity $activity = null, ?Subactivity $subactivity = null): void
{
$api = app(NamiSettings::class)->login();
$response = $api->putMember([
'firstname' => $member->firstname,
'lastname' => $member->lastname,
'joined_at' => $member->joined_at,
'birthday' => $member->birthday,
'send_newspaper' => $member->send_newspaper,
'address' => $member->address,
'zip' => $member->zip,
'location' => $member->location,
'nickname' => $member->nickname,
'other_country' => $member->other_country,
'further_address' => $member->further_address,
'main_phone' => $member->main_phone,
'mobile_phone' => $member->mobile_phone,
'work_phone' => $member->work_phone,
'fax' => $member->fax,
'email' => $member->email,
'email_parents' => $member->email_parents,
'gender_id' => optional($member->gender)->nami_id,
'confession_id' => $member->confession ? $member->confession->nami_id : Confession::firstWhere('is_null', true)->nami_id,
'region_id' => optional($member->region)->nami_id,
'country_id' => $member->country->nami_id,
'fee_id' => $member->getNamiFeeId(),
'nationality_id' => $member->nationality->nami_id,
'group_id' => $member->group->nami_id,
'first_activity_id' => $activity ? $activity->nami_id : null,
'first_subactivity_id' => $subactivity ? $subactivity->nami_id : null,
'id' => $member->nami_id,
'version' => $member->version,
]);
Member::withoutEvents(function () use ($response, $member, $api) {
$member->update(['nami_id' => $response['id']]);
app(MemberPullAction::class)->api($api)->member($member->group->nami_id, $member->nami_id)->execute();
});
}
}

View File

@ -4,7 +4,9 @@ namespace App\Member;
use App\Activity;
use App\Group;
use App\Member\Actions\NamiPutMemberAction;
use App\Setting\NamiSettings;
use App\Subactivity;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
@ -29,8 +31,10 @@ class MemberRequest extends FormRequest
public function rules()
{
return [
'first_activity_id' => Rule::requiredIf(fn () => 'POST' == $this->method()),
'first_subactivity_id' => Rule::requiredIf(fn () => 'POST' == $this->method()),
...'POST' === $this->method() ? [
'first_activity' => 'exclude|required',
'first_subactivity' => 'exclude|required',
] : [],
'subscription_id' => Rule::requiredIf(function () {
if ('POST' != $this->method()) {
return false;
@ -84,7 +88,11 @@ class MemberRequest extends FormRequest
'group_id' => Group::where('nami_id', $settings->default_group_id)->firstOrFail()->id,
]);
if ($this->input('has_nami')) {
CreateJob::dispatch($member);
NamiPutMemberAction::run(
$member,
Activity::findOrFail($this->input('first_activity_id')),
Subactivity::find($this->input('first_subactivity_id')),
);
}
}
@ -97,10 +105,10 @@ class MemberRequest extends FormRequest
$member->save();
if ($this->input('has_nami') && null === $member->nami_id) {
CreateJob::dispatch($member);
NamiPutMemberAction::run($member->fresh(), null, null);
}
if ($this->input('has_nami') && null !== $member->nami_id && $namiSync) {
UpdateJob::dispatch($member->fresh());
NamiPutMemberAction::run($member->fresh(), null, null);
}
if (!$this->input('has_nami') && null !== $member->nami_id) {
DeleteJob::dispatch($member->nami_id);

View File

@ -4,10 +4,14 @@ namespace App\Member;
use App\Activity;
use App\Subactivity;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* @property int $count
*/
class Membership extends Model
{
use HasFactory;
@ -23,4 +27,44 @@ class Membership extends Model
{
return $this->belongsTo(Subactivity::class);
}
/**
* @param Builder<Membership> $query
*
* @return Builder<Membership>
*/
public function scopeIsAgeGroup(Builder $query): Builder
{
return $query->whereHas('subactivity', fn ($builder) => $builder->where('is_age_group', true));
}
/**
* @param Builder<Membership> $query
*
* @return Builder<Membership>
*/
public function scopeIsMember(Builder $query): Builder
{
return $query->whereHas('activity', fn ($builder) => $builder->where('is_member', true));
}
/**
* @param Builder<Membership> $query
*
* @return Builder<Membership>
*/
public function scopeIsLeader(Builder $query): Builder
{
return $query->whereHas('activity', fn ($builder) => $builder->where('has_efz', true));
}
/**
* @param Builder<Membership> $query
*
* @return Builder<Membership>
*/
public function scopeTrying(Builder $query): Builder
{
return $query->whereHas('activity', fn ($builder) => $builder->where('is_try', true));
}
}

View File

@ -1,73 +0,0 @@
<?php
namespace App\Member;
use App\Confession;
use App\Setting\NamiSettings;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class UpdateJob implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
public int $memberId;
public Member $member;
public function __construct(Member $member)
{
$this->memberId = $member->id;
}
/**
* Execute the job.
*
* @return void
*/
public function handle(NamiSettings $settings)
{
$this->member = Member::find($this->memberId);
if (!$this->member->hasNami) {
return;
}
$response = $settings->login()->putMember([
'firstname' => $this->member->firstname,
'lastname' => $this->member->lastname,
'joined_at' => $this->member->joined_at,
'birthday' => $this->member->birthday,
'send_newspaper' => $this->member->send_newspaper,
'address' => $this->member->address,
'zip' => $this->member->zip,
'location' => $this->member->location,
'nickname' => $this->member->nickname,
'other_country' => $this->member->other_country,
'further_address' => $this->member->further_address,
'main_phone' => $this->member->main_phone,
'mobile_phone' => $this->member->mobile_phone,
'work_phone' => $this->member->work_phone,
'fax' => $this->member->fax,
'email' => $this->member->email,
'email_parents' => $this->member->email_parents,
'gender_id' => optional($this->member->gender)->nami_id,
'confession_id' => $this->member->confession ? $this->member->confession->nami_id : Confession::firstWhere('is_null', true)->id,
'region_id' => optional($this->member->region)->nami_id,
'country_id' => $this->member->country->nami_id,
'fee_id' => $this->member->getNamiFeeId(),
'nationality_id' => $this->member->nationality->nami_id,
'id' => $this->member->nami_id,
'group_id' => $this->member->group->nami_id,
'version' => $this->member->version,
]);
Member::withoutEvents(function () use ($response) {
$this->member->update(['version' => $response['version']]);
});
}
}

View File

@ -5,19 +5,11 @@ namespace App\Membership;
use App\Http\Controllers\Controller;
use App\Member\Member;
use App\Member\Membership;
use App\Membership\Requests\StoreRequest;
use App\Setting\NamiSettings;
use Illuminate\Http\RedirectResponse;
class MembershipController extends Controller
{
public function store(Member $member, StoreRequest $request, NamiSettings $settings): RedirectResponse
{
$request->persist($member, $settings);
return redirect()->back();
}
public function destroy(Member $member, Membership $membership, NamiSettings $settings): RedirectResponse
{
$api = $settings->login();

View File

@ -1,52 +0,0 @@
<?php
namespace App\Membership\Requests;
use App\Activity;
use App\Member\Member;
use App\Setting\NamiSettings;
use App\Subactivity;
use Illuminate\Foundation\Http\FormRequest;
use Zoomyboy\LaravelNami\Data\Membership;
class StoreRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
];
}
public function persist(Member $member, NamiSettings $settings): void
{
$from = now()->startOfDay();
$namiId = $settings->login()->putMembership($member->nami_id, Membership::fromArray([
'startsAt' => $from,
'groupId' => $member->group->nami_id,
'activityId' => Activity::find($this->input('activity_id'))->nami_id,
'subactivityId' => optional(Subactivity::find($this->input('subactivity_id')))->nami_id,
]));
$member->memberships()->create([
...$this->input(),
...['nami_id' => $namiId, 'group_id' => $member->group->id, 'from' => $from],
]);
$member->syncVersion();
}
}

View File

@ -12,6 +12,31 @@ class ActivityFactory extends Factory
{
protected $model = Activity::class;
/** @var array<int, string> */
private array $tries = [
'Schnuppermitgliedschaft',
];
/** @var array<int, string> */
private array $members = [
'€ Mitglied',
'Schnuppermitgliedschaft',
];
/** @var array<int, string> */
private array $filterableActivities = [
'€ Mitglied',
'€ passive Mitgliedschaft',
'€ KassiererIn',
'€ LeiterIn',
'Schnuppermitgliedschaft',
];
/** @var array<int, string> */
private array $efz = [
'€ LeiterIn',
];
/**
* Define the model's default state.
*
@ -32,6 +57,12 @@ class ActivityFactory extends Factory
public function name(string $name): self
{
return $this->state(['name' => $name]);
return $this->state([
'name' => $name,
'is_try' => in_array($name, $this->tries),
'is_member' => in_array($name, $this->members),
'is_filterable' => in_array($name, $this->filterableActivities),
'has_efz' => in_array($name, $this->efz),
]);
}
}

View File

@ -21,4 +21,9 @@ class ConfessionFactory extends Factory
'is_null' => false,
];
}
public function inNami(int $namiId): self
{
return $this->state(['nami_id' => $namiId]);
}
}

View File

@ -2,8 +2,10 @@
namespace Database\Factories\Member;
use App\Activity;
use App\Group;
use App\Member\Membership;
use App\Subactivity;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
@ -30,4 +32,15 @@ class MembershipFactory extends Factory
{
return $this->state(['nami_id' => $namiId]);
}
public function in(string $activity, int $activityNamiId, ?string $subactivity = null, ?int $subactivityNamiId = null): self
{
$instance = $this->for(Activity::factory()->name($activity)->inNami($activityNamiId));
if ($subactivity) {
$instance = $instance->for(Subactivity::factory()->name($subactivity)->inNami($subactivityNamiId));
}
return $instance;
}
}

View File

@ -24,4 +24,9 @@ class NationalityFactory extends Factory
'nami_id' => $this->faker->randomNumber(),
];
}
public function inNami(int $namiId): self
{
return $this->state(['nami_id' => $namiId]);
}
}

View File

@ -12,6 +12,25 @@ class SubactivityFactory extends Factory
{
protected $model = Subactivity::class;
/** @var array<int, string> */
private array $filterableSubactivities = [
'Biber',
'Wölfling',
'Jungpfadfinder',
'Pfadfinder',
'Vorstand',
'Rover',
];
/** @var array<int, string> */
private array $ageGroups = [
'Biber',
'Wölfling',
'Jungpfadfinder',
'Pfadfinder',
'Rover',
];
/**
* Define the model's default state.
*
@ -35,13 +54,17 @@ class SubactivityFactory extends Factory
return $this->state(['is_age_group' => true]);
}
public function name(string $name): self
{
return $this->state(['name' => $name]);
}
public function filterable(): self
{
return $this->state(['is_filterable' => true]);
}
public function name(string $name): self
{
return $this->state([
'name' => $name,
'is_filterable' => in_array($name, $this->filterableSubactivities),
'is_age_group' => in_array($name, $this->ageGroups),
]);
}
}

View File

@ -16,7 +16,7 @@ class CreateMembershipsTable extends Migration
Schema::create('memberships', function (Blueprint $table) {
$table->id();
$table->foreignId('group_id')->constrained();
$table->foreignId('member_id')->constrained();
$table->unsignedBigInteger('member_id');
$table->unsignedInteger('nami_id')->nullable();
$table->datetime('from');
$table->timestamps();

View File

@ -98,7 +98,7 @@ export default {
var _self = this;
var options = {
onFinish() {
onSuccess() {
_self.single = null;
_self.mode = null;
},

View File

@ -9,7 +9,8 @@ use App\Initialize\Actions\InitializeFormAction;
use App\Member\Controllers\MemberResyncController;
use App\Member\MemberConfirmController;
use App\Member\MemberController;
use App\Membership\MembershipController;
use App\Membership\Actions\MembershipDestroyAction;
use App\Membership\Actions\MembershipStoreAction;
use App\Payment\AllpaymentController;
use App\Payment\PaymentController;
use App\Payment\SendpaymentController;
@ -33,7 +34,8 @@ Route::group(['middleware' => 'auth:web'], function (): void {
->name('member.singlepdf');
Route::get('/sendpayment', [SendpaymentController::class, 'create'])->name('sendpayment.create');
Route::get('/sendpayment/pdf', [SendpaymentController::class, 'send'])->name('sendpayment.pdf');
Route::apiResource('member.membership', MembershipController::class);
Route::post('/member/{member}/membership', MembershipStoreAction::class)->name('membership.store');
Route::delete('/member/{member}/membership/{membership}', MembershipDestroyAction::class)->name('membership.destroy');
Route::resource('member.course', CourseController::class);
Route::get('/member/{member}/efz', ShowEfzDocumentAction::class)->name('efz');
Route::get('/member/{member}/resync', MemberResyncController::class)->name('member.resync');

View File

@ -24,7 +24,7 @@ class DeleteTest extends TestCase
$response->assertRedirect('/member');
Queue::assertPushed(DeleteJob::class, fn ($job) => $job->memberId === $member->id);
Queue::assertPushed(DeleteJob::class, fn ($job) => 123 === $job->namiId);
$this->assertDatabaseMissing('members', [
'id' => $member->id,
]);
@ -52,9 +52,8 @@ class DeleteTest extends TestCase
$this->withoutExceptionHandling()->login()->loginNami();
$member = Member::factory()->defaults()->inNami(123)->create();
dispatch(new DeleteJob($member));
dispatch(new DeleteJob(123));
app(MemberFake::class)->assertDeleted(123, Carbon::parse('yesterday'));
$this->assertNull($member->fresh()->nami_id);
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Tests\Feature\Member;
use App\Activity;
use App\Member\Member;
use App\Subactivity;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;
class EditTest extends TestCase
{
use DatabaseTransactions;
public function testItDisplaysEditPage(): void
{
$this->withoutExceptionHandling();
$this->login()->loginNami();
$member = Member::factory()->defaults()->create(['firstname' => 'Max']);
$activity = Activity::factory()->hasAttached(Subactivity::factory()->name('Biber'))->name('€ Mitglied')->create();
$subactivity = $activity->subactivities->first();
$response = $this->get(route('member.edit', ['member' => $member]));
$this->assertInertiaHas('Biber', $response, "subactivities.{$activity->id}.{$subactivity->id}");
$this->assertInertiaHas('€ Mitglied', $response, "activities.{$activity->id}");
$this->assertInertiaHas('Max', $response, 'data.firstname');
$this->assertInertiaHas('edit', $response, 'mode');
$this->assertInertiaHas(false, $response, 'conflict');
}
}

View File

@ -0,0 +1,90 @@
<?php
namespace Tests\Feature\Member;
use App\Actions\MemberPullAction;
use App\Activity;
use App\Confession;
use App\Country;
use App\Fee;
use App\Gender;
use App\Group;
use App\Letter\BillKind;
use App\Member\Actions\NamiPutMemberAction;
use App\Member\Member;
use App\Nationality;
use App\Payment\Subscription;
use App\Region;
use App\Subactivity;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;
use Zoomyboy\LaravelNami\Fakes\MemberFake;
class NamiPutMemberActionTest extends TestCase
{
use DatabaseTransactions;
public function testItPutsAMember(): void
{
Fee::factory()->create();
$this->withoutExceptionHandling()->login()->loginNami();
$country = Country::factory()->create();
$gender = Gender::factory()->create();
$region = Region::factory()->create();
$nationality = Nationality::factory()->inNami(565)->create();
$subscription = Subscription::factory()->create();
$billKind = BillKind::factory()->create();
$group = Group::factory()->inNami(55)->create();
$confession = Confession::factory()->inNami(567)->create(['is_null' => true]);
app(MemberFake::class)->createsSuccessfully(55, 993);
$this->stubIo(MemberPullAction::class, fn ($mock) => $mock);
$activity = Activity::factory()->hasAttached(Subactivity::factory()->name('Biber')->inNami(55))->name('Leiter')->inNami(6)->create();
$subactivity = $activity->subactivities->first();
$member = Member::factory()
->for($country)
->for($subscription)
->for($region)
->for($nationality)
->for($billKind)
->for($gender)
->for($group)
->create();
NamiPutMemberAction::run($member, $activity, $subactivity);
app(MemberFake::class)->assertCreated(55, [
'ersteTaetigkeitId' => 6,
'ersteUntergliederungId' => 55,
'konfessionId' => 567,
]);
$this->assertDatabaseHas('members', [
'nami_id' => 993,
]);
}
public function testItMergesExistingData(): void
{
$this->withoutExceptionHandling()->login()->loginNami();
$group = Group::factory()->inNami(55)->create();
$confession = Confession::factory()->inNami(567)->create(['is_null' => true]);
$member = Member::factory()
->defaults()
->inNami(556)
->create();
$this->stubIo(MemberPullAction::class, fn ($mock) => $mock);
app(MemberFake::class)->shows(55, 556, [
'missingkey' => 'missingvalue',
'kontoverbindung' => ['a' => 'b'],
])->updates(55, 556, ['id' => 556]);
NamiPutMemberAction::run($member, null, null);
app(MemberFake::class)->assertUpdated(55, 556, [
'kontoverbindung' => '{"a":"b"}',
'missingkey' => 'missingvalue',
]);
$this->assertDatabaseHas('members', ['nami_id' => 556]);
}
}

View File

@ -0,0 +1,57 @@
<?php
namespace Tests\Feature\Membership;
use App\Group;
use App\Member\Member;
use App\Member\Membership;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;
use Zoomyboy\LaravelNami\Fakes\MemberFake;
use Zoomyboy\LaravelNami\Fakes\MembershipFake;
class DestroyTest extends TestCase
{
use DatabaseTransactions;
public function setUp(): void
{
parent::setUp();
Carbon::setTestNow(Carbon::parse('2022-02-03 03:00:00'));
$this->login()->loginNami();
}
public function testItDestroysAMembership(): void
{
$this->withoutExceptionHandling();
app(MembershipFake::class)
->destroysSuccessfully(6, 1300)
->shows(6, [
'id' => 1300,
'gruppierungId' => 1400,
'taetigkeitId' => 1,
'untergliederungId' => 6,
'aktivVon' => '2017-02-11 00:00:00',
'aktivBis' => null,
]);
app(MemberFake::class)->shows(1400, 6, ['version' => 1506]);
$member = Member::factory()
->defaults()
->for(Group::factory()->inNami(1400))
->has(Membership::factory()->inNami(1300)->in('€ Mitglied', 1, 'Rover', 6))
->inNami(6)
->create();
$response = $this->from('/member')->delete("/member/{$member->id}/membership/{$member->memberships->first()->id}");
$response->assertRedirect('/member');
$this->assertEquals(1506, $member->fresh()->version);
$this->assertDatabaseMissing('memberships', [
'member_id' => $member->id,
'nami_id' => 1300,
]);
app(MembershipFake::class)->assertDeleted(6, 1300);
}
}